假设我有三种方法:
void Foo(MemoryStream v) {Console.WriteLine ("MemoryStream");}
void Foo(Stream v) {Console.WriteLine ("Stream");}
void Foo(object v) {Console.WriteLine ("object");}
我调用方法Foo
传递开放泛型类型的第一个参数:
void Bar<T>()
{
Foo(default(T)); //just to show the scenario
//default(T) or new T() doesn't make a difference, null is irrelevant here
}
我想调用MemoryStream
重载,因此我使用Bar
关闭通用类型的方法MemoryStream
:
Bar<MemoryStream>();
但调用object
重载。如果我将通用约束添加到Foo签名where T : Stream
,则调用Stream
版本。
有没有办法根据开放式通用类型MemoryStream
调度方法调用T
重载?
我不想使用Delegate.CreateDelegate
或其他Reflection API。只是在C#语言的手段。我可能在语言本身内遗漏了一些东西。
使用值类型作为封闭泛型类型并使用静态方法尝试此方案。
答案 0 :(得分:8)
您所描述的内容称为“模板专业化”,但在C#中不起作用。它在C ++中可用,但仍然没有进入C#。
“C# generic interface specialization”已经回答了这个问题。简短的版本就是你不能这样做。您可以解决它强制运行时解析,但在这种情况下使用泛型是没有意义的。泛型应该用于在不同类型上使用相同的代码。
也许还有另一种方法可以做你真正想要的事情。我在实现策略或模板方法模式时遇到类似的情况,我希望大多数代码在一般情况下工作,但修改一些特定的步骤。
在这种情况下,最好将自定义步骤作为接口注入您的类,甚至是Func&lt;&gt;实际创建“模板方法”时,专门处理行为的对象。
当然,还有很多其他方法可以做到这一点,其中一些方法比其他方法更能解决具体问题
答案 1 :(得分:7)
这只能使用动态绑定来完成,例如像这样:
void Bar<T>(T value)
{
dynamic parameter = value;
Foo(parameter);
}
请注意,动态分派使用实际运行时对象的实际运行时类型来执行方法分派,因此必须有一个对象。如果值为null
,则无效。
答案 2 :(得分:3)
也许是这样的:
void Bar<T>()
{
if(typeof(T) == typeof(Stream))
Foo(default(T) as Stream); //just to show the scenario
}
答案 3 :(得分:2)
这不是一个“漂亮”的答案(事实上,因为这有点颠覆了泛型的意图,很难在语言中找到一个漂亮的答案),但你也许可以通过字典来编码过载查找: / p>
static readonly Dictionary<Type, Action<object>> overloads
= new Dictionary<Type, Action<object>> {
{typeof(Stream), o => Foo((Stream)o)},
{typeof(MemoryStream), o => Foo((MemoryStream)o)}
};
public static void Bar<T>() {
Action<object> overload;
if (overloads.TryGetValue(typeof(T), out overload)) {
overload(default(T));
} else {
Foo((object)default(T));
}
}
这不好,我不推荐它。
为了便于维护,您可以将overloads
填充移动到静态构造函数/类型初始化器,并通过反射填充它。另请注意,这仅适用于完全 T
- 如果有人使用意外类型(例如Bar<NetworkStream>
)则无效 - 尽管您可能会在基础上循环-types(但即便如此,它也没有很好的支持接口等)。
考虑到所有事情,这种方法没有太多建议。我可能会建议从不同的角度处理整个问题(即不需要这样做)。
答案 4 :(得分:0)
默认(T)将始终返回null,如果T是数值类型,则返回零。
因此,任何时候都不会返回一个对象,您可以使用它来调用Foo方法的重载版本。
如此简短的回答你无法做到这一点,你必须找到其他方法来调用重载方法。
答案 5 :(得分:0)
我遇到了同样的问题,我所知道的独特解决方案是试用它,直到我得到null
之外的其他内容。然后,我在编译时会有正确的类型,编译器会知道调用正确的重载。我找不到另一种方法来实现这种'运行时多态'。
为了避免使用字典或类似交换机的解决方案(可靠性差,如Marc所指出的那样),只需调用Method((dynamic) o)
,DLR将根据运行时类型调用正确的重载方法。
请记住:
1)提供可能最顶级类型的默认重载;
2)注意在运行时解析类型时的任何歧义(即两个独立的接口和一个使用两者的实现);
3)处理null
案例。
您可以详细了解here。
希望我帮助过。