当你不知道类型时如何指定一个有类型的变量

4

我有一个针对BusinessObject类的扩展方法:

public static class Extensions
{
    public static T Clone<T>(this T obj)
        where T: BusinessObject<T>, new()
    {
        T newObj = new T();
        var props = newObj.Properties;

        foreach (var p in props)
            newObj.Properties[p.Name].SetValue(newObj, obj.Properties[p.Name].GetValue(obj));

        return newObj;
    }
}

那个方法的内容很棒,但是我有一个非泛型的BusinessObject类和一个泛型的对应类。我的很多代码都在非泛型变量中传递泛型对象的实例(请不要问我20个关于为什么的问题)。到目前为止,我还没有遇到问题,因为当我调用这个扩展方法时,它期望一个泛型版本。
调用Clone方法的代码使用了一个包含BusinessObject变量实例的BusinessObject变量。如何将变量转换为实际的类型?换句话说:
Customer cust = new Customer();   // Customer derives from BusinessObject<Customer>
var CustomerClone = cust.Clone(); // This works

BusinessObject obj = cust;
var clone = obj.Clone(); // This doesn't work

在这个例子中,我知道'obj'是一个客户端,但在我的实际方法中,我不知道它可能是任何数量的派生类型。我可以通过使用GetType()轻松地找出派生类型是什么,但如何在运行时将变量转换为该类型呢?


有什么阻止你使用MemberwiseClone的吗? - leppie
@leppie 'var props = newObj.Properties' 正在获取需要克隆的属性。Properties 是一个 BusinessObjectProperties 变量,这是我创建的一个类,用于保存 PropertyInfo 以及一些其他属性的元信息。并非所有内容都会被复制。 - Brandon Moore
1个回答

2
在您的情况下,我建议“去掉 T” - 您没有使用它来完成其他方式无法完成的重要任务(例如 Activator.CreateInstance)。您可以提供两个 API - 一个通用的,一个非通用的,以便于方便的强制转换。例如:
BusinessObject newObj = (BusinessObject)Activator.CreateInstance(obj.GetType());

这也非常关键,因为你想要的T实际上不是T声明,而是具体的T。这意味着:如果你有一个SuperCustomer : Customer,但将其放置在Customer变量中,然后调用Clone,你希望得到一个新的SuperCustomer。然而,T却是Customer。使用GetType()会更可靠。
另一个有用的技巧是dynamic,它是从非泛型到泛型的巧妙转换方式。考虑以下例子:
dynamic foo = 123;
Bar(foo);

void Bar<T>(T bar) {
   Console.WriteLine(typeof(T));
}

将会写出 System.Int32。它已经在编译器只知道 object 的情况下,即时地翻转成了正确的 Tdynamic 大部分实现为 object,带有一些花哨的东西)。

然而,需要强调的是 - 我不会在这里使用那个:我只会写:

public static BusinessObject Clone(this BusinessObject obj)
{
    BusinessObject newObj = (BusinessObject)
          Activator.CreateInstance(obj.GetType());
    var props = newObj.Properties;

    foreach (var p in props)
        newObj.Properties[p.Name].SetValue(newObj,
          obj.Properties[p.Name].GetValue(obj));

    return newObj;
}

如果我确实需要泛型,那么我将使用dynamic作为我的备选策略。


是的,我尝试过了,但它没有正常工作(但已编译)。当我需要调用泛型版本的属性时,它却在非泛型版本上调用了Properties,但我认为我只需要使用GetType()和反射来获取正确的方法,就像你说的那样。 - Brandon Moore
感谢您提供的动态提示,虽然在这种情况下,代码可能需要移植到2008年进行一个项目,所以现在可能会尝试避免这种情况。 - Brandon Moore
@Brandon 如果 Properties 根据泛型和非泛型 API 返回不同的内容,那么我认为这是一个 bug;你应该将其多态化 (virtual),以便返回相同的内容。 - Marc Gravell
是的,我也在苦苦挣扎...它是虚拟的,但是在通用版本中的一个方法会将其设置。 (如果我将非泛型版本标记为抽象,则无法在其上使用扩展方法)。 但是,如果变量被声明为“Foo”,即使它包含具有重写虚拟属性的更派生对象,它也不会返回来自“Foo”的属性,对吗?还是我在这一点上混淆了? - Brandon Moore
好的,我一直在努力弄清楚你在最后代码部分的回答中想表达什么......你是想将T作为返回类型吗?如果是这样,你是否忘记将其设置为通用方法?我有点困惑...... - Brandon Moore
@BrandonMoore 抱歉,打错字了 - 正在修复。 - Marc Gravell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接