Click here to Skip to main content
15,885,914 members
Please Sign up or sign in to vote.
4.00/5 (2 votes)
See more:
Hello,

I have two C# classes. Right now, both are the same, but later one might have some members the other has not...

I want to copy my class source to my class target but dynamically.

I wrote a void to do so.
It can already step through the sub-classes, but I don't know how to pass them, I only get the type/declaration with my void. Any help / idea please?

My code here:

C#
 void copyObject(object source, object target)
{
     if (source.GetType() == (typeof(string)))
     {
         target = source;
     }
     else
     if (source.GetType() == (typeof(int)))
     {
         target = source;
     }
         else
         if (source.GetType() == (typeof(IntPtr)))
         {
             target = source;
         }
             else
             {
                 FieldInfo[] fifsource = source.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public ); // | BindingFlags.NonPublic
                 FieldInfo[] fiftarget = target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public ); // | BindingFlags.NonPublic
                 for (int i = 0; i < fifsource.Length; i++)
                 {
                     if (fifsource.GetType() == fiftarget.GetType())
                     {
                         if (i < fiftarget.Length)
                         {
                             copyObject(fifsource.GetValue(i), fiftarget.GetValue(i));

                         }
                     }
                 }


             }
     }


Thanks a lot!

Eric





I changed my code, now I runs through but the target is still empty after that:

The single strings and ints now are copied to target, but target lost its sense somehow when I am doing like this,
and afterwards my target is empty...


<pre lang="cs">void copyObject(object source, object target)
{
    if (source != null && target != null)
    {
    if (source.GetType() == (typeof(string)))
    {
        target = source;
    }
    else
    if (source.GetType() == (typeof(int)))
    {
        target = source;
    }
        else
        if (source.GetType() == (typeof(IntPtr)))
        {
            target = source;
        }
        else
        {
            FieldInfo[] fifsource = source.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
            FieldInfo[] fiftarget = target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
            for (int i = 0; i < fifsource.Length; i++)
            {
                if (fifsource.GetType() == fiftarget.GetType())
                {
                    if (i < fiftarget.Length)
                    {
                        object psource = source.GetType().GetFields()[i].GetValue(source);
                        object ptarget = target.GetType().GetFields()[i].GetValue(target);
                        if (ptarget == null && psource.GetType() == (typeof(string)))
                        {
                            ptarget = string.Empty;
                        }
                        copyObject(psource, ptarget);
                    }
                }
            }
        }
            }
    }




Hello again, sorry to disturb again. I improved my solution but it is still not working.
I am using ref now so I don't lose the reference of the objects.
I only have problems when setting the found values now.

Two pieces of code now, the first has some comments, this seemed to work but after that
the target class was still empty (I guess the way I tried again the objects lose their reference).
So I decided to add a new function copySubObject
to set the values, but I have problems setting them into the right place...

I believe this can be done...please help me out.
I need to do this or a similar way, it is not my decision.

C#
void refcopyObject(ref object source,ref object target,object svalue,object tvalue)
{
    if (source != null && target != null)
    {
        if (source.GetType() == (typeof(string)))
        {
            target = source;
        }
        else
            if (source.GetType() == (typeof(int)))
            {
                target = source;
            }
            else
                if (source.GetType() == (typeof(IntPtr)))
                {
                    target = source;
                }
                else
                {
                    FieldInfo[] fifsource = source.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
                    FieldInfo[] fiftarget = target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
                    if (fifsource.Length > 0)
                    {
                        for (int i = 0; i < fifsource.Length; i++)
                        {
                            if (fifsource.GetType() == fiftarget.GetType())
                            {
                                if (i < fiftarget.Length)
                                {
                                    object psource = source.GetType().GetFields()[i];
                                    object ptarget = target.GetType().GetFields()[i];
                                    object vsource = source.GetType().GetFields()[i].GetValue(source);
                                    object vtarget = target.GetType().GetFields()[i].GetValue(target);
                                    refcopyObject(ref psource, ref ptarget, vsource, vtarget);
                                }
                            }
                        }
                    }
                    else
                    {
                        //Unten angekommen
                        copySubObject(ref source, ref target, svalue, tvalue);
                        ////So gehts nicht, dann wird die Referenz wieder verloren
                        //FieldInfo[] fifs = svalue.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
                        //if (fifs.Length > 0)
                        //{
                        //    FieldInfo[] fift = tvalue.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
                        //    for (int i = 0; i < fifs.Length; i++)
                        //    {
                        //        if (fifs.GetType() == fift.GetType())
                        //        {
                        //            if (i < fift.Length)
                        //            {
                        //                object psource = svalue.GetType().GetFields()[i].GetValue(svalue);
                        //                object ptarget = tvalue.GetType().GetFields()[i].GetValue(tvalue);
                        //                if (ptarget == null)
                        //                {
                        //                    //Ganz unten angekommen, Problem bei Listen
                        //                    if (psource.GetType() == (typeof(string)))
                        //                    {
                        //                        tvalue.GetType().GetFields()[i].SetValue(tvalue,psource);
                        //                    }
                        //                    if (psource.GetType() == (typeof(int)))
                        //                    {
                        //                        tvalue.GetType().GetFields()[i].SetValue(tvalue, psource);
                        //                    }
                        //                }
                        //                else
                        //                {
                        //                    refcopyObject(ref psource, ref ptarget, null, null);
                        //                }
                        //            }
                        //        }
                        //    }
                        //}
                    }
                }
    }
}


The new routine gets the references and the values (which are structs or lists)

C#
void copySubObject(ref object source, ref object target, object svalue, object tvalue)
{
    if (source != null && target != null)
    {
        FieldInfo[] fifs = svalue.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
        if (fifs.Length > 0)
        {
            FieldInfo[] fift = tvalue.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public); // | BindingFlags.NonPublic
            for (int i = 0; i < fifs.Length; i++)
            {
                if (fifs.GetType() == fift.GetType())
                {
                    if (i < fift.Length)
                    {
                        object psource = svalue.GetType().GetFields()[i].GetValue(svalue);
                        object ptarget = tvalue.GetType().GetFields()[i].GetValue(tvalue);
                        if (ptarget == null)
                        {
                            //Ganz unten angekommen, Problem bei Listen
                            if (psource.GetType() == (typeof(string)))
                            {
                                target.GetType().GetFields()[i].SetValue(tvalue, psource);
                                //tvalue.GetType().GetFields()[i].SetValue(tvalue, psource);
                            }
                            if (psource.GetType() == (typeof(int)))
                            {
                                target.GetType().GetFields()[i].SetValue(tvalue, psource);
                                //tvalue.GetType().GetFields()[i].SetValue(tvalue, psource);
                            }
                        }
                    }
                }
            }
        }
     }
}
Posted
Updated 4-May-11 6:11am
v4

I have two C# classes. Right now, both are the same, but later one might have some members the other has not

If both classes are "almost" the same, why don't you use class inheritance? Make a base class which will contain the common stuff and make a second class which will add new features.

And I suggest to use types in your copy function instead of object and reflection. You don't need reflection here. You can provide several implementations of the Copy function, each of them will accept different types.
You can implement a Copy method in your base class for example, and add a few more in the derived class.
 
Share this answer
 
v2
Comments
EriBeh 4-May-11 7:57am    
Sorry, this is not possible here.
The types can be different, there maybe new types.
Both classes have subclasses of different own made type, Structs with ints and strings.

So I think my idea is 100% good. I just need to work out how to get the real sub-object instead of the class implementation into the copy function?

I don't know which types the classes may contain in future.

But thanks for the suggestion.
Olivier Levrey 4-May-11 8:07am    
I think you should reconsider your design. If you have similar fields in different classes, it means you can group them into a base class or/and interfaces. You can implement a Copy function in each Interface for example, and the Copy function of the inherited class will call the different Copy functions from each Interface it inherits from.
BobJanova 4-May-11 10:35am    
I must echo the 'why are you wanting to do this' questions. Why can't you just use ClassA? Or inherit from it if you want extra functionality, or compose it (i.e. a ClassB which has a public ClassA A property)?
EriBeh 4-May-11 8:39am    
This is hard to explain.
The source is a class that comes from somewhere else.
From time to time there are strings or structs added to the source.
Then I will add them to my target.
And I don't want to copy each single Class/Struct/string in one line, I would get around 300 lines then.

So I need to copy everything from source (if it is also existing in target) to target.
You can copy an object with any of the following methods:

0) Create a constructor in both classes that accepts an object of the other class as a parameter, and manually copy the properties:

C#
public class ClassA
{
    public ClassA(ClassB b)
    {
        this.myproperty = b.property;
    }
}

public class ClassB
{
    public ClassB(ClassA a)
    {
        this.myproperty = a.property;
    }
}


1) Overload the = operator for each object, and set the properties manually

C#
public class ClassA
{
    public ClassA(ClassB b)
    {
    }

    public ClassA(ClassB b)
    {
        myproperty = b.property;
    }

    public static implicit operator ClassA(ClassB b)
    {
        ClassA = new ClassA(b);

        // or
        ClassA a = new ClassA();
        a.myproperty = b.property;

        return a;
    } 
}

public class ClassB
{
    public ClassB()
    {
    }

    public ClassB(ClassA a)
    {
        this.myproperty = a.property;
    }

    public static implicit operator Rectangle(ClassA a)
    {
        ClassB = new ClassB(a);

        // or
        ClassB b = new ClassB();
        b.myproperty = a.property;

        return b;
    } 
}


EDIT ==============

Since you can't change ClassA or ClassB, just write an extension method:

C#
public static class ExtensionMethods
{
    public static ClassA SetTo(this ClassA a, ClassB b)
    {
        ClassA newA = new ClassA();
        newA.property = b.property;
        return newA;
    }

    // Or this way (you can't do it both ways without changing 
    // the method name or the parameter list) because you can't 
    // overload just by changing the return type.
    public static void SetTo(this ClassA a, ClassB b)
    {
        a.property = b.property;
    }
}
 
Share this answer
 
v4
Comments
Hemant__Sharma 4-May-11 8:45am    
I love implicit and explicit casting my 5
EriBeh 4-May-11 8:53am    
Thanks for that suggestion.

My problem is I cannot do anything with the source (Class A).
It is updated somewhere else from time to time.
Then I change my target class and update a webservice based on my target class.
I have no influence on source (Class A).
I just need to copy everything from source to target, if it exists in target.



Source has structs and lists of structs. The structs contain strings and int and sometimes new stuff is added...
You need to have a common ground which both classes can land on. And utilize some generics. I mean you have to comprimise to have some level of common grounds.
Take distributed simulation for instance. If a piece of simulation (which is developped by some other group or company) is not developped according to some design which others have no knowledge of then they cannot communicate.

In your case: if both sides have class members as simple list of objects (it is dumb but generic) what you need can be accomplished. Use a common XML, text, anything works or you'd be banging your head everytime other party updates thier design.

Edit 3: (Previous edits combined)

MIDL
public static void copyObject(object source,object target)
 {
     if (source != null && target != null)
     {
         Dictionary<string, FieldInfo> fifsource =
             source.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public).ToDictionary(fi => fi.Name);
         Dictionary<string, System.Reflection.FieldInfo> fiftarget =
             target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public).ToDictionary(fi => fi.Name);
         if( fifsource.Count == 0 && source.GetType() == target.GetType() )
         {
             // if you are here someone either called this with 0 public field class
             // or function is called with simple types which results in NOTHING because
             // it is call by value writing "target = source;" is pointless
             // either case anyway. If it fits your tatse change function to 
             // copyObject(ref object source,ref object target)
             // and call it accordingly.
             target = source;
         }
         else
         {
             foreach (KeyValuePair<string,FieldInfo> fi in fifsource)
             {
                 if (fiftarget.ContainsKey(fi.Key) && fiftarget[fi.Key].FieldType == fi.Value.FieldType)
                     fi.Value.SetValue(target,fiftarget[fi.Key].GetValue(source));
             }
         }
     }
 }

Test Case:
C#
public class subItem
{
    public int i;
    public double d;
    public int[] iArray;
}
public class AClass
{
    public int m_dummy;
    public subItem su;
    private void Initialize()
    {
        m_dummy = 0;
        su.i = 0;
        su.d = 1e-300;
        su.iArray = new int[] { 1, 1, 1, 1, 1 };
    }
    public AClass()
    {
        su = new subItem();
        Initialize();
    }
}
public class BClass : AClass
{
    private void Initialize()
    {
        m_dummy = 1;
        su.i = 1;
        su.d = 1e300;
        su.iArray = new int[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
    }
    public BClass()
    {
        Initialize();
    }
}

Result:
C#
AClass a = new AClass();
AClass b = new BClass();
objectCopier.copyObject(a, b);
Works. I am curious how cruel bosses can be :)
 
Share this answer
 
v8
Comments
EriBeh 4-May-11 12:03pm    
Please believe me, I need to copy everything from Class A to Class B when the sub-item exists in Class B. This is my job right now. It is not my decision. And I have no influence on Class A.

And I believe it can be done this way. I added new code, using Ref now which seems to be much better, I only have problems setting the values I found...
EriBeh 5-May-11 2:38am    
Hello yesotaso,

added your code like this:

void copyDict(object source, object target)
{
Dictionary<string, fieldinfo=""> fifsource = source.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public).ToDictionary(fi => fi.Name);
Dictionary<string, fieldinfo=""> fiftarget = target.GetType().GetFields(BindingFlags.Instance | BindingFlags.Public).ToDictionary(fi => fi.Name);
foreach (KeyValuePair<string, fieldinfo=""> fi in fifsource)
{
if (fiftarget.ContainsKey(fi.Key) && fiftarget[fi.Key].FieldType == fi.Value.FieldType)
fiftarget[fi.Key].SetValue(target, fi.Value.GetValue(source));
}
}

Whas it thought this way? I get an error:
Error 65 'System.Array' does not contain a definition for 'ToDictionary' and no extension method 'ToDictionary' accepting a first argument of type 'System.Array' could be found (are you missing a using directive or an assembly reference?)

Isn't there a soultion to get my last posted code working? I just need to get the value to the right place there... thanks :-)
EriBeh 5-May-11 4:31am    
By the way, this only works for one subclass, not for subclasses with subclasses or structs, right?
yesotaso 5-May-11 14:05pm    
Sorry for late answer it was a busy day. Basicly yes it copies 1 class with "simple" attributes to another. If an attribute is complex (a class-struct) type you can recurse for that field aswell.
Side note: I did a few tests without recursion. that piece just copy fine. As I mentioned earlier array types or collection types may prove to be difficult.
For the Error: Which version of VS do you use? ".ToDictionary(fi => fi.Name)" does not work if you do not have VS2008 or 2010. And dont forget to put null checks.
EriBeh 6-May-11 4:23am    
I added LinQ namespace then it was okay, I have VS 2010.
But the solution does not work. The SubClasses have the same name and same values, but different namspace. So the SetValue does not work this way but gives an exception.
I mapped the target class manually, value after value. But it would be cool to find a solution how to do that dynamically ;-) Thanks for those suggestions and hints, I learned a lot.

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900