Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/302.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 通用方法Don';t调用类型为'的方法;T';_C#_.net_Generics - Fatal编程技术网

C# 通用方法Don';t调用类型为'的方法;T';

C# 通用方法Don';t调用类型为'的方法;T';,c#,.net,generics,C#,.net,Generics,假设我有两个班: class a { public void sayGoodbye() { Console.WriteLine("Tschüss"); } public virtual void sayHi() { Console.WriteLine("Servus"); } } class b : a { new public void sayGoodbye() { Console.WriteLine("Bye"); } override public voi

假设我有两个班:

class a
{
    public void sayGoodbye() { Console.WriteLine("Tschüss"); }
    public virtual void sayHi() { Console.WriteLine("Servus"); }
}

class b : a
{
    new public void sayGoodbye() { Console.WriteLine("Bye"); }
    override public void sayHi() { Console.WriteLine("Hi"); }
}
如果调用要求从类“a”派生类型“T”的泛型方法:

void call<T>() where T : a
void call(),其中T:a
然后在该方法中,我对类型为“T”的实例调用方法,该方法调用绑定到类型为“a”,就好像该实例被强制转换为“a”一样:

call<b>();
...
void call<T>() where T : a
{
    T o = Activator.CreateInstance<T>();
    o.sayHi(); // writes "Hi" (virtual method)
    o.sayGoodbye(); // writes "Tschüss"
}
call();
...
void call(),其中T:a
{
T o=Activator.CreateInstance();
o、 sayHi();//写入“Hi”(虚拟方法)
o、 saybye();//写“Tschüss”
}
通过使用反射,我能够获得预期的结果:

call<b>();
...
void call<T>() where T : a
{
    T o = Activator.CreateInstance<T>();
    // Reflections works fine:
    typeof(T).GetMethod("sayHi").Invoke(o, null); // writes "Hi"
    typeof(T).GetMethod("sayGoodbye").Invoke(o, null); // writes "Bye"
}
interface Ia
{
    void sayGoodbye();
    void sayHi();
}
...
class a : Ia // 'a' implements 'Ia'
...
call<b>();
...
void call<T>() where T : Ia
{
    T o = Activator.CreateInstance<T>();
    o.sayHi(); // writes "Hi"
    o.sayGoodbye(); // writes "Bye"
}
call();
...
void call(),其中T:a
{
T o=Activator.CreateInstance();
//反射效果很好:
typeof(T).GetMethod(“sayHi”).Invoke(o,null);//写“Hi”
typeof(T).GetMethod(“saybye”).Invoke(o,null);//写“Bye”
}
此外,通过使用类“a”的接口,我获得了预期的结果:

call<b>();
...
void call<T>() where T : a
{
    T o = Activator.CreateInstance<T>();
    // Reflections works fine:
    typeof(T).GetMethod("sayHi").Invoke(o, null); // writes "Hi"
    typeof(T).GetMethod("sayGoodbye").Invoke(o, null); // writes "Bye"
}
interface Ia
{
    void sayGoodbye();
    void sayHi();
}
...
class a : Ia // 'a' implements 'Ia'
...
call<b>();
...
void call<T>() where T : Ia
{
    T o = Activator.CreateInstance<T>();
    o.sayHi(); // writes "Hi"
    o.sayGoodbye(); // writes "Bye"
}
接口Ia
{
虚空说再见();
void sayHi();
}
...
a类:Ia/'a'实现“Ia”
...
call();
...
void call(),其中T:Ia
{
T o=Activator.CreateInstance();
o、 sayHi();//写“Hi”
o、 saybye();//写“再见”
}
等效的非泛型代码也可以正常工作:

call();
...
void call()
{
    b o = Activator.CreateInstance<b>();
    o.sayHi(); // writes "Hi"
    o.sayGoodbye(); // writes "Bye"
}
call();
...
无效调用()
{
b o=Activator.CreateInstance();
o、 sayHi();//写“Hi”
o、 saybye();//写“再见”
}
如果我将泛型约束更改为“b”,也会发生同样的情况:

call<b>();
...
void call<T>() where T : b
{
    T o = Activator.CreateInstance<T>();
    o.sayHi(); // writes "Hi"
    o.sayGoodbye(); // writes "Bye"
}
call();
...
void call(),其中T:b
{
T o=Activator.CreateInstance();
o、 sayHi();//写“Hi”
o、 saybye();//写“再见”
}

编译器似乎正在生成对约束中指定的基类的方法调用,所以我想我理解发生了什么,但这不是我所期望的。这真的是正确的结果吗?

说再见不是虚拟的

编译器只“知道”T是类型a。它会在一天内打电话说再见


在类型b上,您重新定义了saybye,但编译器不知道类型b。它不可能知道a的所有衍生物。您可以通过使saybaye虚拟化来告诉编译器saybaye可能被重写。这将导致编译器以一种特殊的方式调用saybye。

正如您所看到的,方法隐藏与多态性不同。只要从B向下转换到A,就可以调用方法的A版本

对于泛型方法,当T被限制为类型a时,编译器无法知道它是否可能是其他类型,因此它使用隐藏方法而不是在a上定义的方法是非常意外的。方法隐藏是为了方便或互操作性;它与替代行为无关;为此,您需要多态性和虚拟方法


编辑:


我认为这里的基本混淆实际上是泛型和C++样式模板。在.NET中,泛型类型只有一个代码基。创建专用泛型类型不涉及为特定类型发出新代码。这与C++不同,其中模板专门化实际上涉及创建和编译附加代码,以便它将对指定的类型真正地专业化。

< P> <代码>新< /Cord>关键字是C语言中的一种黑客攻击。它与多态性相矛盾,因为调用的方法取决于C++引用的类型。 泛型是一种通用类型:编译器只输出一个泛型类(或方法)。泛型不能在编译时使用提供的实际类型替换
t
,这需要为每个类型参数编译一个单独的泛型实例,但可以通过使用空“空格”生成一个类型来工作。在泛型类型中,编译器接着在不知道特定参数类型的情况下解决这些“空白”上的操作。因此,它只使用它已经拥有的信息;也就是说,除了全局事实之外,您还提供了约束,例如“一切都是对象”

所以当你说

void call<T>() where T : a {
    T o = Activator.CreateInstance<T>();
    o.sayGoodbye();//nonvirtual
现在,调用非虚方法会忽略变量的运行时类型。正如预期的那样,您会看到调用了
a.saybye()

比较而言,C++模板确实以你期望的方式工作——它们实际上在编译时扩展模板,而不是用“空白”来定义单个定义,因此特定模板实例可以使用只能用于该专门化的方法。事实上,即使在运行时,CLR也避免实际实例化模板的特定实例:因为所有调用对于特定类都是虚拟的(不需要显式实例化)或非虚拟的(同样,实例化没有意义),CLR可以使用相同的字节(甚至可能是相同的x86代码)来覆盖多种类型。这并不总是可能的(例如,对于值类型),但是对于节省内存和JIT时间的引用类型

还有两件事。。。 首先,您的调用方法使用
Activator
——这不是必需的;您可以使用一个例外约束
new()
,它执行相同的操作,但用于编译时检查:

void call<T>() where T : a, new() {
    T o = new T();
    o.sayGoodbye();
void call(),其中T:a,new(){
T o=新的T();
o、 说再见();
尝试编译
call()
将在编译时失败,并显示人类可读的消息

其次,如果泛型仅仅是空白的话,那么它似乎在很大程度上是毫无意义的——毕竟,为什么不一直简单地处理
a
类型的变量呢?好吧,虽然在编译时你不能依赖于
a
的子类在泛型方法中可能具有的任何细节,但你仍然强制所有
t
都是of同一子类,它特别允许使用众所周知的容器,如
List
——其中即使
List
永远不能依赖
int
内部,对于
List
的用户来说,仍然可以方便地