3

I'm working in c# with several workspaces that have one specific class which his always the same in each workspace. I would like to be able have a copy of this class to be able to work with it without dealing with namespaces differences. example :

namespace1 {
    class class1{
        public class2;
    }

    class class2{
        public string;
    }

}

namespace2 {
    class class1{
        public class2;
    }

    class class2{
        public string;
    }
}

In my copied Class I've got a function to copy all data's to one of the namespace's class. It's working if i only have c# standard types. I got exeption ( "Object does not match target type." ) as soon as I'm dealing with class2 object (which is also from different namespaces)

public Object toNamespaceClass(Object namespaceClass)
{
    try
    {
        Type fromType = this.GetType();
        Type toType = namespaceClass.GetType();

        PropertyInfo[] fromProps = fromType.GetProperties();
        PropertyInfo[] toProps = toType.GetProperties();

        for (int i = 0; i < fromProps.Length; i++)
        {
            PropertyInfo fromProp = fromProps[i];
            PropertyInfo toProp = toType.GetProperty(fromProp.Name);
            if (toProp != null)
            {
                toProp.SetValue(this, fromProp.GetValue(namespaceClass, null), null);
            }
        }

    }
    catch (Exception ex)
    {
    }
    return namespaceClass;
}

Anyone do have any idea of how to deal with this kind of "recursivity reflection".

I hope eveything is understandable.

Thanks, Bye!

Edit : I think i got it solved (at least in my mind), I'll try the solution back at work tomorrow. Taking my function out of my class and using it recursively if a property is not a standard type is maybe the solution.

Alexis
  • 131
  • 2
  • 11
  • Why just not implement on CloneLikeOtherClass() function that returns that type exactly like you want, avoiding in this way to deal with reflection. ? – Tigran Jul 06 '11 at 15:37
  • 1
    Is there any reason you are not making class2 a member of a class library, then sharing that between your workspaces? Seems like your are going to alot of trouble to rework a fundamental principle of OO development. – Perception Jul 06 '11 at 15:41
  • @Tigran : I need to use it with lots of namespace so doing one function per namespace is not the best way. @Perception : I can't modify the class I use it's part of someone else job. – Alexis Jul 06 '11 at 17:48
  • Are these classes serializable? I mean by using standart BinarySerialization. – Tigran Jul 06 '11 at 18:13
  • I never used binary serialization so I don't know, for sure they are XML serializable because used in some webservices. – Alexis Jul 06 '11 at 21:42

7 Answers7

7

BinaryFormatter does not work in .Net 4.5 as it remembers from what type of class the instance was created. But with JSON format, it does not. JSON serializer is implemented by Microsoft in DataContractJosnSerializer.

This works:

    public static T2 DeepClone<T1, T2>(T1 obj)
    {
        DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T1));
        DataContractJsonSerializer deserializer = new DataContractJsonSerializer(typeof(T2));

        using (var ms = new MemoryStream())
        {
            serializer.WriteObject(ms, obj);
            ms.Position = 0;

            return (T2)deserializer.ReadObject(ms);
        }
    }

and uses as follows:

   var b = DeepClone<A, B>(a);
Peter Kerr
  • 1,649
  • 22
  • 33
  • It's a good idea but the serializer has at least one limitation: it does not serialize if the object contains datetimes out of range Datetime.minValue and Datetime.maxValue. – Anthony Brenelière Feb 25 '16 at 14:05
6

I had the similar problem. I got to use similar classes but different in terms of namespace only. As a quick solution I performed below steps and it works.

  1. Serialize source class into XML.
  2. In SerializedXML replace source namespace with the target one.
  3. DeSerialize with target type.

I know there is performance overhead with above way but it is quick to implement and error free.

Sundeep
  • 61
  • 1
  • 2
2

I got it solved , just to let you know how I did it : This solution is sot perfect because it handle only 1 dimensions array not more.

public static Object CopyObject(Object from , Object to)
{
    try
    {
        Type fromType = from.GetType();
        Type toType = to.GetType();

        PropertyInfo[] fromProps = fromType.GetProperties();
        PropertyInfo[] toProps = toType.GetProperties();

        for (int i = 0; i < fromProps.Length; i++)
        {
            PropertyInfo fromProp = fromProps[i];
            PropertyInfo toProp = toType.GetProperty(fromProp.Name);
            if (toProp != null)
            {
                if (toProp.PropertyType.Module.ScopeName != "CommonLanguageRuntimeLibrary")
                {
                    if (!toProp.PropertyType.IsArray)
                    {
                        ConstructorInfo ci = toProp.PropertyType.GetConstructor(new Type[0]);
                        if (ci != null)
                        {
                            toProp.SetValue(to, ci.Invoke(null), null);
                            toProp.SetValue(to, gestionRefelexion.CopyObject(fromProp.GetValue(from, null), toProp.GetValue(to, null)), null);
                        }
                    }
                    else
                    {
                        Type typeToArray = toProp.PropertyType.GetElementType();
                        Array fromArray = fromProp.GetValue(from, null) as Array;
                        toProp.SetValue(to, copyArray(fromArray, typeToArray), null);
                    }
                }
                else
                {
                    toProp.SetValue(to, fromProp.GetValue(from, null), null);
                }
            }
        }
    }
    catch (Exception ex)
    {
    }
    return to;
}

public static Array copyArray(Array from, Type toType)
{
    Array toArray =null;
    if (from != null)
    {
        toArray= Array.CreateInstance(toType, from.Length);

        for (int i = 0; i < from.Length; i++)
        {
            ConstructorInfo ci = toType.GetConstructor(new Type[0]);
            if (ci != null)
            {
                toArray.SetValue(ci.Invoke(null), i);
                toArray.SetValue(gestionRefelexion.CopyObject(from.GetValue(i), toArray.GetValue(i)), i);
            }
        }
    }
    return toArray;
}

Hope this can help some people. Thanks for helping everyone. Cheers

takrl
  • 6,356
  • 3
  • 60
  • 69
Alexis
  • 131
  • 2
  • 11
0

This problem can be elegantly solves using Protocol Buffers because Protocol Buffers do not hold any metadata about the type they serialize. Two classes with identical fields & properties serialize to the exact same bits.

Here's a little function that will change from O the original type to C the copy type

static public C DeepCopyChangingNamespace<O,C>(O original)
{
    using (MemoryStream ms = new MemoryStream())
    {
        Serializer.Serialize(ms, original);
        ms.Position = 0;
        C c = Serializer.Deserialize<C>(ms);
        return c;
    }
}

usage would be

namespace1.class1 orig = new namespace1.class1();

namespace2.class1 copy = 
    DeepCopyChangingNamespace<namespace1.class1, namespace2.class1>(orig);
Eric J.
  • 147,927
  • 63
  • 340
  • 553
0
public static T DeepClone<T>(T obj)
{
 using (var ms = new MemoryStream())
 {
   var formatter = new BinaryFormatter();
   formatter.Serialize(ms, obj);
   ms.Position = 0;

   return (T) formatter.Deserialize(ms);
 }
}

from here

Community
  • 1
  • 1
EgorBo
  • 6,120
  • 3
  • 35
  • 40
0

Two identical or similar objects from different namespaces ?

You have this:

namespace Cars
{
  public class car {
    public string Name;
    public void Start() { ... }
  }
} 


namespace Planes
{
  public class plane {
    public string Name;
public void Fly() { ... }
  }
} 

Time to apply some class inheritance:

namespace Vehicles
{
  public class vehicle
  {
    public string Name;
  } // class
} // namespace

using Vehicles;
namespace Cars
{
  public class car: vehicle
  {
    public string Name;
    public void Start() { ... }
  }  // class
} // namespace

using Vehicles;
namespace Planes
{
  public class plane: vehicle
  {
    public void Fly() { ... }
  }
} 

And to copy, there is a copy method or constructor, but, I prefer a custom one:

namespace Vehicles
{
  public class vehicle {
    public string Name;

    public virtual CopyFrom (vehicle Source)
    {
      this.Name = Source.Name;
      // other fields
    }
  } // class

} // namespace 

Cheers.

umlcat
  • 4,091
  • 3
  • 19
  • 29
  • @Bashir This is a way to solve the problem, the developer may or not may try this solution ;-) – umlcat Jul 06 '11 at 15:43
  • sorry but that's not what I have exactly the same class but with different namespace inheritance is not the way to do it. I can't modify those class because it's not part of my application I'm just using it. – Alexis Jul 06 '11 at 17:45
0

You either need to refactor all of your duplicate classes into a single shared class or implement a common interface that all of your various classes implement. If you really can't modify the underlying types, create a subclass for each that implements your common interface.

Yes, you can do it with reflection... but you really shouldn't because you end up with brittle, error prone, code.

Yaur
  • 7,333
  • 1
  • 25
  • 36
  • I though about that solution but it never ended successfully. In fact those duplicate classes are each part of different webservices that I consume, and I aimed to make generic function to deal with those common class in each webservices (session/authentification/security ...) If I implement an interface which is implemented by every others, I'll not be able to consume webservices with that interface. For the mment I'm working with the answer I ticked as it works fine, but it's maybe not the best solution if we are talking about performance. – Alexis Jul 11 '11 at 08:20