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
大小写

你可以阅读更多关于它的内容


希望我能帮上忙。

for
MemoryStream的默认值(T)
null
,因此调用对象重载。我觉得还可以。@StephaneDelcroix如果我添加
其中的T:stream
,那么
默认值(T)
仍然为空,但调用了其他方法。检查msdn以获得过载解决方案。编译器可以猜测表达式的类型,即使结果值为null。示例
Foo((MemoryStream)null)
将调用正确的重载,并将
null
作为参数。应该注意的是,“基于不同的
T
做不同的事情”与“泛型”的意图几乎完全相反,泛型本质上被定义为“对任何不同的
T
应用相同的模式”-在这里,泛型本身对您没有帮助,这并不奇怪……您所建议的基本上是一个虚拟调用。恐怕唯一的方法是实现您自己的vtable模式或在c#中使用动态调度。顺便说一句,用
new T()替换
default(T)
,仍然调用
对象
重载。这不是关于null,而是关于泛型和重载!不知道为什么会被否决;这不是一个很好的方法,但那是因为