【问题标题】:Handle generics处理泛型
【发布时间】:2014-04-29 05:00:27
【问题描述】:
class Order { ... }

class OrderA : Order { ... }

class OrderB : Order { ... }

class OrderC : Order { ... }

class OrderD : Order { ... }


private Order GetOrder() { ... }

我怎样才能有一个方法将返回的 Order 对象动态转换为任何特定对象:

private T GetSpecificOrder<T>(T order) : where T : Order
{
    ...
}

我想这样称呼它:

var myOrder = GetOrder();
var specificOrder = GetSpecificOrder<Order>(myOrder);
HandleOrder(specificOrder);

我希望将 specificOrder 对象实例化为子类之一,因此我可以这样称呼它:

HandleOrder(OrderA o) { ... }
HandleOrder(OrderB o) { ... }
HandleOrder(OrderC o) { ... }
HandleOrder(OrderD o) { ... }

我做错了什么?

【问题讨论】:

  • 如果从GetOrder 返回的订单不是正确的类型,您不能只将其转换为A 或B。您必须实际进行映射并实例化正确的类型。如果类型不匹配,则不能向上投射。

标签: c# .net generics visual-studio-2008 .net-3.5


【解决方案1】:

听起来您希望在执行时发生重载解决方案。这不会发生...除非您使用dynamic,这可能是您最简单的解决方案:

dynamic order = GetOrder(orderId);
HandleOrder(order); // The right overload will be picked at execution time

另一种选择是使用double-dispatch / the visitor pattern,但我个人觉得这有点笨拙。

另一种选择是像这样创建Dictionary&lt;Type, Action&lt;Order&gt;&gt;

private static readonly Dictionary<Type, Action<Order>> orderHandlers = 
    new Dictionary<Type, Action<Order>>
{
    { typeof(OrderA), order => HandleOrder((OrderA) order) },
    { typeof(OrderB), order => HandleOrder((OrderB) order) },
    { typeof(OrderC), order => HandleOrder((OrderC) order) },
    { typeof(OrderD), order => HandleOrder((OrderD) order) },
};

然后只需获取相关的委托并调用它:

orderHandlers[order.GetType()](order);

【讨论】:

  • 是否可以这样做:bool result = orderHandlers[order.GetType()](order); ??
  • @HélderGonçalves:当然,只需将其从 Action&lt;Order&gt; 更改为 Func&lt;Order, bool>。 (您的问题没有显示任何有关方法的返回类型...)
  • 很像stackoverflow.com/a/6883766/168719(我以前也有类似的问题)
【解决方案2】:

你不能用泛型来做到这一点:尖括号中的任何东西都必须是编译时已知的类型,所以这一行

var specificOrder = GetSpecificOrder<Order>(myOrder);

需要演员才能成为OrderAOrderB等。

但是,至少有两种方法可以解决这种情况:

  1. 通过将Accept 方法添加到Order 及其子类来使用Visitor Pattern,或者
  2. 通过使用dynamic,在这种情况下,运行时将为您执行正确的调度。

这里是如何实现#1:

interface IOrderHandler {
    void HandleOrder(OrderA o);
    void HandleOrder(OrderB o);
    void HandleOrder(OrderC o);
    void HandleOrder(OrderD o);
}
abstract class Order { abstract void Accept(IOrderHandler h); }

class OrderA : Order { void Accept(IOrderHandler h) {h.HandleOrder(this);} }

class OrderB : Order { void Accept(IOrderHandler h) {h.HandleOrder(this);} }

class OrderC : Order { void Accept(IOrderHandler h) {h.HandleOrder(this);} }

class OrderD : Order { void Accept(IOrderHandler h) {h.HandleOrder(this);} }

...

var myOrder = GetOrder();
IOrderHandler handler = ... // Obtain the handler
myOrder.Accept(handler);    // This will call the correct HandleOrder

这里是如何实现#2:

dynamic myOrder = GetOrder();
HandleOrder(myOrder); // Will dispatch based on the runtime type

【讨论】:

  • 我使用的是 .NET 3.5 和 Visual Studio 2008,所以我猜动态是不可能的吧?
  • @HélderGonçalves 对,dymamic 直到 4.0 才可用。但是访问者是语言中立的。
【解决方案3】:

试试这个...

private T GetSpecificOrder<T>(T order)
    {
     return (T)Convert.ChangeType(T, typeof(T));
    }

【讨论】:

    【解决方案4】:

    您需要调用 GetSpecificOrder 作为所需的派生类:

    var specificOrder = GetSpecificOrder<OrderA>(myOrder);
    

    或者投射它:

    var specificOrder = (OrderA)GetSpecificOrder<Order>(myOrder);
    

    然后您可以为给定的派生类分配任何独特的属性。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 2022-01-28
      相关资源
      最近更新 更多