3

我有一些类需要能够将所有公共属性从一个对象复制到另一个对象。
每个类都有一组可能与任何其他类不同的公共属性。

例子:

class Base
{
  // Common properties/methods...
  public void Copy<T>(T data) where T : Base
  {
     // ...
  }
}

class D1 : Base
{
  public int ID
  {
    get;
    set;
  }
}

class D2 : Base
{
  public string Name
  {
    get;
    set;
  }
}

通过谷歌搜索,我已经阅读了这些方法:

  • 使用反射
  • 生成 IL 代码
  • 序列化

所有这些都非常复杂或非常缓慢,或者有时两者兼而有之。
我错过了什么吗?还有其他方法可以访问原始this指针吗?

编辑:
我会澄清。
T 是调用类的类型。例如,如果它被 D1 调用,则 T 将始终为 D1。
通用的原因是我真的不知道 T 是什么。
我错过了什么吗?
我应该只Base data用作参数吗?

4

5 回答 5

6

您缺少的是,当您所说的只是 T 是 Base 时,您要求编译器知道 T 可能是 D1 和 D2 类型之一。怎么可能知道您的对象的属性甚至类型,因为该信息仅在运行时才知道。即使你可以foreach (PropertyInfo in this.Properties),它也会在运行时找出这些属性的名称,所以和反射一样慢,因为它还能怎么办?(这反射,只是更漂亮的语法)。它无法知道哪些属性是常见的,直到它知道它正在处理什么类型并且你说过“我直到运行时才告诉你”所以答案是“我必须查看运行时”,即反射。

其次,仅仅因为 D1 和 D2 可能都有一个名为 Size 的属性,并不意味着它们是相同的属性(除非该属性存在于一个共同的祖先中)。

例如,

  • ArtilleryAmmo.Shell 和 PecanNut.Shell。
  • AcmeCorp.Staff 和 GandolfTheWizard.Staff
  • California.State 和 MyBrokenEngine.State
  • LoudSpeaker.Volume 和 MassiveCrater.Volume
  • Cave.Bats 和 BasketballGame.Bats

等等等等

于 2010-06-30T08:34:05.327 回答
2

您可以使用架构更改来解决此问题,并使用“PropertyBag”来存储每个类的属性。

PropertyBag 本质上是一个Dictionary<string, object>您可以给一条数据命名并将其添加到包中的地方。缺点是所有内容都被强制转换为object,所以它不是非常安全的类型,而且还有很多装箱/拆箱,加上作为名称的字符串在编译时不会得到检查,所以拼写错误是一个持续的威胁。

当你在类上定义一个属性时,你从类的属性包中存储/检索项目:

public int MyProperty
{
    get
    {
        return (int)_propertyBag["MyProperty"];
    }
    set
    {
        if(_propertyBag.Keys.Contains("MyProperty"))
        {
          _propertyBag["MyProperty"] = value;
        }
        else
        {
          _propertyBag.Add("MyProperty", value);
        }
    }
}

因此,现在要聚合派生类的所有属性,您可以公开它们的“原始”PropertyBag 并遍历它。

就像我之前说的,PropertyBags 不是类型安全的,所以如果层次结构中有两个类具有相同的属性名称但类型不同,那么你就会遇到麻烦。

编辑:如果你关心性能,你将不得不以多种方式实现并测试不同的实现——我不能老实说 PropertyBag 是否真的比使用反射更快。

于 2010-06-30T08:41:42.437 回答
1

我认为复制方法应该由派生类 D1、D2 继承,并且它们有责任将自己的属性复制到/从其他类型。

于 2010-06-30T08:41:47.680 回答
1

类中的Copy方法Base只能访问类中定义的属性Base。您可以复制这些属性。

但是如果不使用反射之类的东西,就不能从子类中复制属性。但是即使使用反射,您也需要一些关于不同子类之间属性映射的知识,例如将ID属性复制到Name.

因此,您需要为每个(允许的)子类转换编写单独的实现。

public interface IBaseCopier<TFrom, TTo> where TFrom : Base, TTo : Base
{
  void Copy(TFrom from, TTo to);
}

public class D1ToD2Copier : IBaseCopier<D1, D2>
{
  public void Copy(D1 from, D2 to)
  {
    // Copy properties from the D1 instance to the D2 instance.
  }
}

ICopier<TFrom, TTo>您可以在工厂类中注册所有实现。此类将根据类型参数查找复印机的实现。如果某种类型组合没有复制器,即不支持转换,工厂应该抛出异常。

public class CopierFactory
{
  public ICopier<TFrom, TTo> Create<TFrom, TTo>() where TFrom : Base, TTo : Base
  {
    // Look up the ICopier implementation for the given types.
  }
}

编辑

您可以使用MemberwiseClone方法来创建对象的副本。

public class Base
{
  public static T Copy<T>(T data) where T : Base
  {
    return data.MemberwiseClone() as T;
  }
}

如果您需要对克隆进行更多控制,可以实现ICloneable 接口

注意:您应该意识到您不能将实例克隆D1D2实例中。这就像将一只羊克隆成一匹马。

于 2010-06-30T08:59:22.273 回答
0

我要做的是为 Base 类创建一个扩展方法,例如:

namespace ExtensionMethods
{
    public static class MyExtensions
    {
        public static int CopyTo<T>(this Base source, ref T dest)
        {
          // Use reflection to cycle public properties and if you find equally named ones, copy them.
        }
    }   
}

然后你可以在你的对象中调用它,比如:

  source.CopyTo<ClassType>(ref this);

我没有测试,所以不确定它是否会像描述的那样工作。在我从事的一个大项目中,我做了类似于将 DataRows 转换为实体的事情。

于 2010-06-30T08:46:34.093 回答