C# 查找相同层次结构的两种类型共享的属性 [英] C# Find properties shared by two types of the same hierarchy

查看:60
本文介绍了C# 查找相同层次结构的两种类型共享的属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要将属性值从一个类复制到另一个属于相同基类型的后代.源对象和目标对象可能位于同一继承分支的不同级别,这意味着一个继承自另一个,或者是不同分支的后代,意味着它们共享一个共同的基类型.

I need to copy the value of properties from one class to another that are descendants of the same base type. Source and target object may be on different levels of the same inheritance branch, meaning one is derived from the other, or be descendant of different branches, meaning they share a common base type.

      A
  B1      B2
C1 C2      C3

从上面的结构中,我可能想将所有属性从 A 复制到 C1、C2 到 C3、C3 到 B1 等等.基本上是树中任何可能的组合.显然,我只能复制源类型中存在的属性,也必须存在于目标类型中.

From the structure above I may want to copy all properties from A to C1, C2 to C3, C3 to B1, etc. Basically any possible combination from the tree. Obviously I can only copy properties present in the source type, that must also be present in the target type.

迭代源类型的属性很容易

Iterating the properties of the source type is easy as

var sourceProperties = source.GetType().GetProperties();

但是,我如何检查目标类型上声明了哪个属性?仅按名称检查是不够的,因为它们可能具有不同的类型.同样在过去,我在使用 new 时遇到了重复属性的糟糕经历.

However how do I check which property is declared on the target type? Simply checking by name is not enough, as they might have different types. Also in the past I made bad experiences with duplicate properties using new.

不幸的是,C# 或 .NET 没有内置方法来检查类型是否具有特定的 PropertyInfo,例如 Type.HasProperty(PropertyInfo).我能想到的最好方法是检查该属性是否由共享基类型声明.

Unfortunately C# or .NET has no built-in method to check if a type has a certain PropertyInfo like Type.HasProperty(PropertyInfo). The best I could come up with is to check if the property was declared by a shared base type.

public static void CopyProperties(object source, object target)
{
    var targetType = target.GetType();
    var sharedProperties =source.GetType().GetProperties()
        .Where(p => p.DeclaringType.IsAssignableFrom(targetType));

    foreach (var property in sharedProperties)
    {
        var value = property.GetValue(source);
        if (property.CanWrite)
            property.SetValue(target, value);
    }
}

问题:有更好的解决方案吗?

推荐答案

这是一个不需要继承的解决方案.只要名称和类型匹配,它就会将属性从一个(一种类型的)对象复制到另一个(另一种类型的)对象.

Here's a solution that doesn't require inheritance. It copies properties from one object (of one type) to another (of another type) as long as the names and types match.

您为您希望能够从中复制/复制到的每一对类型创建一个这些属性复制器对象的实例.复制对象一旦被创建就是不可变的,因此它可以是长期存在的、静态的、被多个线程使用(创建后)等.

You create an instance of once of these property copier objects for each pair of types you want to be able to copy from/to. The copier object is immutable once created, so it can be long-lived, static, used from many threads (after created), etc.

这是 PropertyCopier 类的代码.创建此类型的对象时,需要指定源类型和目标类型.

Here's the code for the PropertyCopier class. You need to specify the source and destination types when you create an object of this type.

public class PropertyCopier<TSource, TDest> where TSource : class where TDest : class
{
    private List<PropertyCopyPair> _propertiesToCopy = new List<PropertyCopyPair>();

    public PropertyCopier()
    {
        //get all the readable properties of the source type
        var sourceProps = new Dictionary<string, Tuple<Type, MethodInfo>>();
        foreach (var prop in typeof(TSource).GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            if (prop.CanRead)
            {
                sourceProps.Add(prop.Name, new Tuple<Type, MethodInfo>(prop.PropertyType, prop.GetGetMethod()));
            }
        }

        //now walk though the writeable properties of the destination type
        //if there's a match by name and type, keep track of them.

        foreach (var prop in typeof(TDest).GetProperties(BindingFlags.Instance | BindingFlags.Public))
        {
            if (prop.CanWrite)
            {
                if (sourceProps.ContainsKey(prop.Name) && sourceProps[prop.Name].Item1 == prop.PropertyType)
                {
                    _propertiesToCopy.Add (new PropertyCopyPair(prop.Name, prop.PropertyType, sourceProps[prop.Name].Item2, prop.GetSetMethod()));
                }
            }
        }
    }

    public void Copy(TSource source, TDest dest)
    {
        foreach (var prop in _propertiesToCopy)
        {
            var val = prop.SourceReader.Invoke(source, null);
            prop.DestWriter.Invoke(dest, new []{val});
        }
    }
}

它依赖于一个看起来像这样的帮助器类(这可以被剥离;额外的属性可以帮助调试(并且可能对你有用)).

It relies on a helper class that looks like (this can get stripped down; the extra properties where to help with debugging (and might be useful to you)).

public class PropertyCopyPair
{
    public PropertyCopyPair(string name, Type theType, MethodInfo sourceReader, MethodInfo destWriter)
    {
        PropertyName = name;
        TheType = theType;
        SourceReader = sourceReader;
        DestWriter = destWriter;
    }

    public string PropertyName { get; set; }
    public Type TheType { get; set; }
    public MethodInfo SourceReader { get; set; }
    public MethodInfo DestWriter { get; set; }
}

我还创建了另一个真正简单的测试类:

I also created another real simple class for testing:

public class TestClass
{
    public string PropertyName { get; set; }
    public Type TheType { get; set; }
    public string Other { get; set; }
}

有了所有这些,这段代码练习了复印机类:

With all that in place, this code exercises the copier class:

 var copier = new PropertyCopier<PropertyCopyPair, TestClass>();
 var source = new PropertyCopyPair("bob", typeof(string), null, null);
 var dest = new TestClass {Other = "other", PropertyName = "PropertyName", TheType = this.GetType()};
 copier.Copy(source, dest);

当您运行它时,所有在目标中具有相同名称和类型的属性的源属性都将被复制.

When you run it, all the properties of the source that have a property in the destination that have the same name and type will get copied.

如果要将源类型和目标类型限制为公共基类,可以这样做:

If you want to restrict the source and destination types to a common base class, you can do this:

public class PropertyCopierCommonBase<TSource, TDest, TBase> : PropertyCopier<TSource, TBase>
    where TBase : class where TSource : class, TBase where TDest : class, TBase
{  }

如果您不想要两个类,只需使用上面的三个类型参数和一组通用约束声明原始的 PropertyCopier 类.

If you don't want two classes, just declare the original PropertyCopier class with the three type parameters above, and that set of generic constraints.

这篇关于C# 查找相同层次结构的两种类型共享的属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆