gpt4 book ai didi

C# 查找同一层次结构的两种类型共享的属性

转载 作者:太空宇宙 更新时间:2023-11-03 14:51:53 25 4
gpt4 key购买 nike

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

      A
B1 B2
C1 C2 C3

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

迭代源类型的属性很容易

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

但是,如何检查目标类型上声明了哪个属性?仅仅通过名称检查是不够的,因为它们可能有不同的类型。同样在过去,我使用 new 对重复属性进行了糟糕的体验。

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

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);
}
}

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

最佳答案

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

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

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

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});
}
}
}

它依赖于一个看起来像这样的帮助程序类(这可以被精简;额外的属性可以帮助调试(并且可能对您有用))。

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; }
}

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

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

所有这些都准备就绪后,这段代码将执行复印机类:

 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);

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

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

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

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

关于C# 查找同一层次结构的两种类型共享的属性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51444920/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com