C#使用反射来复制基本类属性(C# Using Reflection to copy base cl

2019-06-17 18:32发布

我想更新从MyObject的所有属性到另一个使用反思。 我进入的问题是,在特定的对象是从基类继承,而不是更新的基类的属性值。

下面的代码拷贝在顶层的属性值。

public void Update(MyObject o)
{
    MyObject copyObject = ...

    FieldInfo[] myObjectFields = o.GetType().GetFields(
    BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

    foreach (FieldInfo fi in myObjectFields)
    {
        fi.SetValue(copyObject, fi.GetValue(o));
    }
}

我一直在寻找,看看是否有任何更多的BindingFlags属性,我可以用它来帮助,但无济于事。

Answer 1:

试试这个:

public void Update(MyObject o)
{
    MyObject copyObject = ...
    Type type = o.GetType();
    while (type != null)
    {
        UpdateForType(type, o, copyObject);
        type = type.BaseType;
    }
}

private static void UpdateForType(Type type, MyObject source, MyObject destination)
{
    FieldInfo[] myObjectFields = type.GetFields(
        BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);

    foreach (FieldInfo fi in myObjectFields)
    {
        fi.SetValue(destination, fi.GetValue(source));
    }
}


Answer 2:

我写这为不同类型的工作太多的扩展方法。 我的问题是,我有一个绑定到ASP MVC形式某些机型上,和其他实体映射到数据库。 理想情况下,我只会有1类,但实体分阶段建设和ASP MVC模型想一次验证整个模型。

下面是代码:

public static class ObjectExt
{
    public static T1 CopyFrom<T1, T2>(this T1 obj, T2 otherObject)
        where T1: class
        where T2: class
    {
        PropertyInfo[] srcFields = otherObject.GetType().GetProperties(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty);

        PropertyInfo[] destFields = obj.GetType().GetProperties(
            BindingFlags.Instance | BindingFlags.Public | BindingFlags.SetProperty);

        foreach (var property in srcFields) {
            var dest = destFields.FirstOrDefault(x => x.Name == property.Name);
            if (dest != null && dest.CanWrite)
                dest.SetValue(obj, property.GetValue(otherObject, null), null);
        }

        return obj;
    }
}


Answer 3:

嗯。 我以为GetFields让你从一路上涨的链成员,你必须显式指定BindingFlags.DeclaredOnly如果你不想继承的成员。 所以,我做了一个快速测试,我是对的。

然后,我发现了一些:

我想更新从MyObject的所有属性到另一个使用反思。 我进入的问题是,在特定的对象是从基类继承,而不是更新的基类的属性值。

下面的代码拷贝在顶层的属性值。

 public void Update(MyObject o) { MyObject copyObject = ... FieldInfo[] myObjectFields = o.GetType().GetFields( BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 

这将只获得 (包括对这种类型的私有字段),而不是性能 。 所以,如果你有这样的层次结构(请原谅的名字!):

class L0
{
    public int f0;
    private int _p0;
    public int p0
    {
        get { return _p0; }
        set { _p0 = value; }
    }
}

class L1 : L0
{
    public int f1;
    private int _p1;
    public int p1
    {
        get { return _p1; }
        set { _p1 = value; }
    }
}

class L2 : L1
{
    public int f2;
    private int _p2;
    public int p2
    {
        get { return _p2; }
        set { _p2 = value; }
    }
}

那么.GetFieldsL2BindingFlags指定将获得f0f1f2_p2 ,但不能p0p1 (这是性能,而不是字段)OR _p0_p1 (这是私人的基类,因此一类型的对象L2 不具有这些字段。

如果要复制的属性,尝试做你正在做什么,但使用.GetProperties代替。



Answer 4:

这没有考虑到与参数帐户的属性,也不考虑可能无法访问私人get / set访问,也没有考虑只读枚举接口,所以这里是一个扩展的解决方案?

我试图转换为C#,但是对于一般的来源没有这样做,我没有把它转换自己的时间。

''' <summary>
''' Import the properties that match by name in the source to the target.</summary>
''' <param name="target">Object to import the properties into.</param>
''' <param name="source">Object to import the properties from.</param>
''' <returns>
''' True, if the import can without exception; otherwise, False.</returns>
<System.Runtime.CompilerServices.Extension()>
Public Function Import(target As Object, source As Object) As Boolean
    Dim targetProperties As IEnumerable(Of Tuple(Of Reflection.PropertyInfo, Reflection.MethodInfo)) =
        (From aPropertyInfo In source.GetType().GetProperties(Reflection.BindingFlags.Public Or Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
         Let propertyAccessors = aPropertyInfo.GetAccessors(True)
         Let propertyMethods = aPropertyInfo.PropertyType.GetMethods()
         Let addMethod = (From aMethodInfo In propertyMethods
                          Where aMethodInfo.Name = "Add" AndAlso aMethodInfo.GetParameters().Length = 1
                          Select aMethodInfo).FirstOrDefault()
         Where aPropertyInfo.CanRead AndAlso aPropertyInfo.GetIndexParameters().Length = 0 _
          AndAlso (aPropertyInfo.CanWrite OrElse addMethod IsNot Nothing) _
          AndAlso (From aMethodInfo In propertyAccessors
                   Where aMethodInfo.IsPrivate _
                    OrElse (aMethodInfo.Name.StartsWith("get_") OrElse aMethodInfo.Name.StartsWith("set_"))).FirstOrDefault() IsNot Nothing
         Select New Tuple(Of Reflection.PropertyInfo, Reflection.MethodInfo)(aPropertyInfo, addMethod))
    ' No properties to import into.
    If targetProperties.Count() = 0 Then Return True

    Dim sourceProperties As IEnumerable(Of Tuple(Of Reflection.PropertyInfo, Reflection.MethodInfo)) =
        (From aPropertyInfo In source.GetType().GetProperties(Reflection.BindingFlags.Public Or Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance)
         Let propertyAccessors = aPropertyInfo.GetAccessors(True)
         Let propertyMethods = aPropertyInfo.PropertyType.GetMethods()
         Let addMethod = (From aMethodInfo In propertyMethods
                          Where aMethodInfo.Name = "Add" AndAlso aMethodInfo.GetParameters().Length = 1
                          Select aMethodInfo).FirstOrDefault()
         Where aPropertyInfo.CanRead AndAlso aPropertyInfo.GetIndexParameters().Length = 0 _
          AndAlso (aPropertyInfo.CanWrite OrElse addMethod IsNot Nothing) _
          AndAlso (From aMethodInfo In propertyAccessors
                   Where aMethodInfo.IsPrivate _
                    OrElse (aMethodInfo.Name.StartsWith("get_") OrElse aMethodInfo.Name.StartsWith("set_"))).FirstOrDefault() IsNot Nothing
         Select New Tuple(Of Reflection.PropertyInfo, Reflection.MethodInfo)(aPropertyInfo, addMethod))
    ' No properties to import.
    If sourceProperties.Count() = 0 Then Return True

    Try
        Dim currentPropertyInfo As Tuple(Of Reflection.PropertyInfo, Reflection.MethodInfo)
        Dim matchingPropertyInfo As Tuple(Of Reflection.PropertyInfo, Reflection.MethodInfo)

        ' Copy the properties from the source to the target, that match by name.
        For Each currentPropertyInfo In sourceProperties
            matchingPropertyInfo = (From aPropertyInfo In targetProperties
                                    Where aPropertyInfo.Item1.Name = currentPropertyInfo.Item1.Name).FirstOrDefault()
            ' If a property matches in the target, then copy the value from the source to the target.
            If matchingPropertyInfo IsNot Nothing Then
                If matchingPropertyInfo.Item1.CanWrite Then
                    matchingPropertyInfo.Item1.SetValue(target, matchingPropertyInfo.Item1.GetValue(source, Nothing), Nothing)
                ElseIf matchingPropertyInfo.Item2 IsNot Nothing Then
                    Dim isEnumerable As IEnumerable = TryCast(currentPropertyInfo.Item1.GetValue(source, Nothing), IEnumerable)
                    If isEnumerable Is Nothing Then Continue For
                    ' Invoke the Add method for each object in this property collection.
                    For Each currentObject As Object In isEnumerable
                        matchingPropertyInfo.Item2.Invoke(matchingPropertyInfo.Item1.GetValue(target, Nothing), New Object() {currentObject})
                    Next
                End If
            End If
        Next
    Catch ex As Exception
        Return False
    End Try

    Return True
End Function


Answer 5:

波格丹Litescu的解决方案的伟大工程,但我还要检查是否可以写入性能。

foreach (var property in srcFields) {
        var dest = destFields.FirstOrDefault(x => x.Name == property.Name);
        if (dest != null)
            if (dest.CanWrite)
                dest.SetValue(obj, property.GetValue(otherObject, null), null);
    }


Answer 6:

我有一个对象是我从一个基础对象派生,并且对于某些场景添加额外的性质。 但想设置在派生类对象的新实例的所有基础对象属性。 基础对象添加更多的特性,即使后来,我不担心加硬编码线设置在派生类对象的基本属性。

由于maciejkow我想出了以下内容:

// base object
public class BaseObject
{
    public int ID { get; set; } = 0;
    public string SomeText { get; set; } = "";
    public DateTime? CreatedDateTime { get; set; } = DateTime.Now;
    public string AnotherString { get; set; } = "";
    public bool aBoolean { get; set; } = false;
    public int integerForSomething { get; set; } = 0;
}

// derived object
public class CustomObject : BaseObject
{
    public string ANewProperty { get; set; } = "";
    public bool ExtraBooleanField { get; set; } = false;

    //Set base object properties in the constructor
    public CustomObject(BaseObject source)
    {
        var properties = source.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance);

        foreach(var fi in properties)
        {
            fi.SetValue(this, fi.GetValue(source));
        }
    }
}

简直可以用这样的:

public CustomObject CreateNewCustomObject(BaseObject obj, string ANewProp, bool ExtraBool)
{
    return new CustomObject(obj)
    {
        ANewProperty = ANewProp,
        ExtraBooleanField = ExtraBool
    };
}

其他的想法我有:

  • 将简单地铸造对象的工作? (CustomObject)baseObject

    (我测试铸造,得到了System.InvalidCastException: 'Unable to cast object of type 'BaseObject' to type 'CustomObject'.'

  • 序列化到JSON字符串和反序列化到CustomObject?

    (I测试序列化/反序列化 - 工作就像一个魅力,但在序列化/反序列化一个明显的滞后)

因此,与反射在派生对象的构造函数设置属性是在我的测试情况下瞬间。 我相信JSON序列化/反序列化也使用反射在anycase,但这样做两次,而在与反射构造转换时才会发生一次。



文章来源: C# Using Reflection to copy base class properties