-1

I'm trying to write some effecient C# code to copy selected class members from object A to object B based on a list of the member names. The class in question will have a combination of string members and class members. Here's an example of the class layouts.

public class Class0
{
    public string C0Prop1 {  get; set; } = "c0.prop1";
}
public class Class1
{
    public string C1Prop1 { get; set; } = "c1.prop1";
    public string C1Prop2 { get; set; } = "c1.prop2";
}
public class Class2
{
    public string C2Prop1 { get; set; } = "c2.prop1";
    public Class1 C2Prop2 { get; set; } = new Class1();
    public string C2Prop3 { get; set; } = "c2.prop3";
    public string C2Prop4 { get; set; } = "c2.prop4";
    public Class0 C2Prop5 { get; set; } = new Class0();
}

I then have a function, foo(), which has a list 'props' which are names of some of the members of Class2.

My goal is to have some tight code to allow me to copy just C2Prop2 and C2Prop4 from the source object to the destination object. Eventually 'props' is passed in to foo() so it will be dynamic and foo() can copy any of the elements.

foo()
{
    BindingFlags _flags = BindingFlags.GetProperty | BindingFlags.IgnoreCase |
                            BindingFlags.Instance | BindingFlags.Static |
                            BindingFlags.Public | BindingFlags.NonPublic |
                            BindingFlags.FlattenHierarchy;
    Class2 source = new Class2();
    Class2 dest = new Class2();
    List<string>    props = new List<string>() {  nameof(Class2.C2Prop2), nameof(Class2.C2Prop4) };

    foreach (string prop in props)
    {
        var s = source.GetType().GetProperty(prop, _flags).GetValue(source, null);
        if (s != null)
        {
            if (String.Compare(prop, nameof(Class2.C2Prop2), true) == 0)
            {   // one option could be json
                dest.C2Prop2 = JsonConvert.DeserializeObject<Class2>(JsonConvert.SerializeObject(s));
            }
            else 
            if (String.Compare(prop, nameof(Class2.C2Prop5), true) == 0)
            {   // another option for copy
                dest.C2Prop5 = source.C2Prop5;
            }
            else
            { // this is a simple string member, just assign it.
                dest.GetType().GetProperty(prop, _flags).SetValue(dest, s.ToString());
            }
        }
    }
}

For this example I'm iterating through the list of 'props' and using PropertyInfo to determine characteristics of the source member and then decide if it's a class or a string and do the copy accordingly. I'm sure there has to be a more optimal way of generically copying a single member of a class object from a source to a destination, but for the life of me I can't quite figure it out.

Any assistance on this would be greatly appreciated!

WebDrive
  • 1,248
  • 12
  • 19
  • Well, to start with don't think that passing arrays of strings will be efficient. Use an enum and bit fields. Second, create a method for each class that clones the fields you name and returns a new object. Trying to do it outside of the object, without prior knowledge of what the object is and contains, calls for the use of the Reflection classes, and they are not fast. – Steve Todd May 07 '19 at 17:00
  • Thanks for the great suggestion @SteveTodd; use of enums and bit fields would be preferred; unfortunately I am bound by design constraints which require my foo() to accept a list of strings containing the names of the members of the class objects. I figure Reflection is going to be part of the solution, so I'm trying to be as effecienct as possible to offset the overhead of using reflection. – WebDrive May 07 '19 at 17:43

1 Answers1

0

I was able to leverage the GetSetMethod() and GetGetMethod() of the PropertyInfo for both the source and target objects to perform a generic copy. Granted this assumes that the members will need to have public getters and setters, but this logic allowed me to minimize the amount of code.

Feel free to recommend improvements.

public void copyAttrs(List<string> props, ref Class2 source, ref Class2 dest)
{
    BindingFlags _flags = BindingFlags.GetProperty | BindingFlags.IgnoreCase |
                            BindingFlags.Instance | BindingFlags.Static |
                            BindingFlags.Public | BindingFlags.NonPublic |
                            BindingFlags.FlattenHierarchy;
    foreach (string prop in props)
    {
        var propSource = source.GetType().GetProperty(prop, _flags);
        var proDest = dest.GetType().GetProperty(prop,_flags);
        if ((propDest != null) && (propSource != null))
        {
            MethodInfo destSetMethod = propDest.GetSetMethod();
            MethodInfo sourceGetMethod = propSource.GetGetMethod();
            if ((destSetMethod != null) && (sourceGetMethod != null))
                destSetMethod.Invoke(dest, new object[] { sourceGetMethod.Invoke(source, null) });
        }
    }
}
WebDrive
  • 1,248
  • 12
  • 19