C# 如何基于封闭泛型类型调用方法重载?
假设我有三种方法:C# 如何基于封闭泛型类型调用方法重载?,c#,generics,C#,Generics,假设我有三种方法: 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(
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
}
但是调用了对象
重载。如果我将通用约束添加到Foo签名中,其中T:Stream
,则调用Stream
版本
是否有一种方法可以根据打开的泛型类型T
向MemoryStream
重载分派方法调用?
我不想使用Delegate.CreateDelegate
或其他反射API。就用C语言。我可能在语言本身中遗漏了一些东西
尝试将值类型作为封闭泛型类型并使用静态方法执行此方案 可能是这样的:
void Bar<T>()
{
if(typeof(T) == typeof(Stream))
Foo(default(T) as Stream); //just to show the scenario
}
void Bar<T>(T value)
{
dynamic parameter = value;
Foo(parameter);
}
void Bar()
{
如果(类型(T)=类型(流))
Foo(默认(T)为流);//仅用于显示场景
}
这不是一个“漂亮”的答案(事实上,因为这有点颠覆泛型的意图,所以很难在语言内部找到一个漂亮的答案),但您可能可以通过字典对重载查找进行编码:
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));
}
}
静态只读字典重载
=新词典{
{typeof(Stream),o=>Foo((Stream)o)},
{typeof(MemoryStream),o=>Foo((MemoryStream)o)}
};
公共静态无效条(){
动作过载;
if(重载.TryGetValue(类型(T),输出重载)){
过载(默认值(T));
}否则{
Foo((对象)默认值(T));
}
}
这不好,我也不推荐。
为了便于维护,您可以将重载
填充移动到静态构造函数/类型初始化器,并通过反射填充它。还请注意,这只适用于精确的T
——如果有人使用意外的类型(例如Bar
),这将不起作用——尽管您可能会循环基本类型(但即使如此,它对接口等也没有很好的支持)
从各方面考虑,这种方法没有多少可取之处。我可能会建议从不同的角度来处理整个问题(即不需要这样做)。您所描述的被称为“模板专门化”,在C#中不起作用。它在C++中是可用的,但是还没有达到C。 这已在“”中得到回答。简而言之,你做不到。您可以通过强制运行时解析来解决这个问题,但在这种情况下,使用泛型没有任何意义。泛型应该用于在不同类型上使用相同的代码 也许有另一种方法来做你真正想做的事。我在实现策略或模板方法模式时也遇到过类似的情况,我希望大多数代码都能在一般情况下工作,但会修改一些特定的步骤 在这种情况下,最好在实际创建“模板方法”时,将自定义步骤作为接口,甚至作为专门化行为的Func对象注入到类中 当然,还有很多其他方法可以做到这一点,其中一些方法在处理特定问题时比其他方法更好当类型T为引用类型时,默认(T)将始终返回null,如果类型T为数值类型,则返回零 因此,在任何时候,它都不会返回一个对象,您可以使用该对象调用重载版本的Foo方法
简而言之,您不能这样做,您必须找到其他方法来调用重载方法。这只能使用动态绑定来完成,例如:
void Bar<T>()
{
if(typeof(T) == typeof(Stream))
Foo(default(T) as Stream); //just to show the scenario
}
void Bar<T>(T value)
{
dynamic parameter = value;
Foo(parameter);
}
空栏(T值)
{
动态参数=数值;
Foo(参数);
}
注意,动态调度使用实际运行时对象的实际运行时类型来进行方法调度,因此必须有一个对象。如果值为
null
,这将不起作用。我也有同样的问题,我知道的唯一解决方案是尝试将其强制转换,直到我得到了除null
以外的其他值。然后,我将在编译时拥有正确的类型,编译器将知道要调用的正确重载。我找不到其他方法来实现这种“运行时多态性”
为了避免使用字典或类似开关的解决方案(如Marc所指出的,可维护性差),只需调用方法((动态)o)
,DLR将根据运行时类型调用正确的重载方法
记住:
1) 提供一个默认重载,尽可能使用最顶级的类型
2) 在运行时解析类型时,请注意任何歧义(即两个独立接口和一个同时使用这两个接口的实现)
3) 句柄null
大小写
你可以阅读更多关于它的内容
希望我能帮上忙。
forMemoryStream的默认值(T)
为null
,因此调用对象重载。我觉得还可以。@StephaneDelcroix如果我添加其中的T:stream
,那么默认值(T)
仍然为空,但调用了其他方法。检查msdn以获得过载解决方案。编译器可以猜测表达式的类型,即使结果值为null。示例Foo((MemoryStream)null)
将调用正确的重载,并将null
作为参数。应该注意的是,“基于不同的T
做不同的事情”与“泛型”的意图几乎完全相反,泛型本质上被定义为“对任何不同的T
应用相同的模式”-在这里,泛型本身对您没有帮助,这并不奇怪……您所建议的基本上是一个虚拟调用。恐怕唯一的方法是实现您自己的vtable模式或在c#中使用动态调度。顺便说一句,用new T()替换default(T)
,仍然调用对象
重载。这不是关于null,而是关于泛型和重载!不知道为什么会被否决;这不是一个很好的方法,但那是因为