使用不同的命名空间复制两个相同的对象(递归反射)

时间:2011-07-06 15:30:50

标签: c# reflection

我在c#中工作,有几个工作区有一个特定的类,在每个工作区中它总是相同的。 我希望能够拥有此类的副本,以便能够使用它而无需处理命名空间差异。 例如:

namespace1 {
    class class1{
        public class2;
    }

    class class2{
        public string;
    }

}

namespace2 {
    class class1{
        public class2;
    }

    class class2{
        public string;
    }
}

在我复制的Class中,我有一个函数将所有数据复制到命名空间的类之一。 如果我只有c#标准类型,它会工作。一旦我处理class2对象(也来自不同的命名空间),我得到了exeption(“对象与目标类型不匹配。”)

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

任何人都知道如何处理这种“递归反射”。

我希望eveything是可以理解的。

谢谢,再见!

编辑: 我想我已经解决了问题(至少在我看来),明天我会尝试解决这个问题。如果属性不是标准类型,可以将我的函数从我的类中取出并递归使用它可能是解决方案。

7 个答案:

答案 0 :(得分:6)

BinaryFormatter在.Net 4.5中不起作用,因为它记得实例创建的类的类型。但是对于JSON格式,它没有。 JSON序列化程序由Microsoft在DataContractJosnSerializer中实现。

这有效:

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

并使用如下:

   var b = DeepClone<A, B>(a);

答案 1 :(得分:5)

我遇到了类似的问题。我必须使用类似的类,但仅在命名空间方面有所不同。作为一种快速解决方案,我在下面的步骤中执行了它。

  1. 将源类序列化为XML。
  2. 在SerializedXML中,将源命名空间替换为目标命名空间。
  3. 目标类型的DeSerialize。
  4. 我知道上面的方法存在性能开销,但实现起来很快且没有错误。

答案 2 :(得分:2)

我解决了,只是为了让你知道我是怎么做到的: 这个解决方案非常完美,因为它只能处理1维数组。

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

希望这可以帮助一些人。 谢谢你们帮助大家。 干杯

答案 3 :(得分: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);
 }
}

来自here

答案 4 :(得分:0)

来自不同命名空间的两个相同或相似的对象?

你有这个:

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


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

应用某些类继承的时间:

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() { ... }
  }
} 

要复制,有一个复制方法或构造函数,但是,我更喜欢自定义方法:

namespace Vehicles
{
  public class vehicle {
    public string Name;

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

} // namespace 

干杯。

答案 5 :(得分:0)

您需要将所有重复的类重构为单个共享类,或者实现所有各个类实现的公共接口。如果你真的无法修改底层类型,请为每个实现公共接口的类创建一个子类。

是的,你可以用反射做到这一点......但是你真的不应该这样做,因为你最终会遇到脆弱,容易出错的代码。

答案 6 :(得分:0)

使用Protocol Buffers可以优雅地解决此问题,因为协议缓冲区不包含有关它们序列化类型的任何元数据。两个相同领域和类别的班级属性序列化为精确相同的位。

这是一个小功能,将从 O 原始类型更改为 C 复制类型

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

用法是

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

namespace2.class1 copy = 
    DeepCopyChangingNamespace<namespace1.class1, namespace2.class1>(orig);