当我使用带有泛型参数的方法来创建另一个对象时,泛型对象不会选择最具体的构造函数。这听起来令人困惑,所以这里有一些示例代码来证明我的意思......
任何人都可以解释为什么这个程序的输出是:
guid <-- easy - no problem here
object <-- WHY? This should also be "guid"?!
...以及如何使通用Add<T>
函数调用C
的正确构造函数?这是代码:
void Main()
{
B b = new B();
C c = new C(Guid.Empty);
b.Add<Guid>(Guid.Empty);
}
public class B
{
List<C> cs = new List<C>();
public void Add<T>(T v) { cs.Add(new C(v)); }
}
public class C
{
public C(Guid c) { Console.WriteLine("guid"); }
public C(object c) { Console.WriteLine("object"); }
}
答案 0 :(得分:10)
重载解析在编译时完成,而不是在运行时。因此,当您从new C(v)
方法调用Add<T>
时,编译器不知道T
实际上将是Guid
,因此它使用唯一保证为的重载兼容,即public C(object c)
答案 1 :(得分:3)
C#中的泛型与C ++模板的工作方式不同 - 它们在编译时不会根据用法进行扩展。创建一个方法,静态解析从中调用的方法。
因此,在Add
中,v
可以是任何类型,因此唯一已知的是继承自object
所以object
的{{1}}构造函数是唯一的候选人。
要获得您想要的行为,您必须添加C
的另一个重载,例如
Add
答案 2 :(得分:0)
public void Add<T>(T v) where T : Guid {
cs.Add(new C(v));
}
当然,现在您只能使用Guid或Guid的扩展名来调用Add。回想起来有点毫无意义。我想你正在寻找什么,你会做一个运行时检查。 if (v is Guid)
我想......对不起,我忘记了C#类型检查操作符是什么。
答案 3 :(得分:0)
如果定义了一个类Foo<T> where T:SomeClass
,则将执行所有方法绑定和运算符重载,就像T
是SomeClass
一样;在没有约束的情况下,它们通常会像Object
那样执行。在评估绑定和重载时,没有机制可以考虑T
的类型。
如果需要有一些特殊行为的类型,可以使用一个或多个方法定义一个私有泛型接口,以对类型T
执行操作,这是一个私有泛型类,它在“通常的“时尚和私有非泛型类,它们以适合某些特殊类型的方式实现接口。私有泛型类应该包含对接口类型的静态引用。对于非特殊类T
,该引用应该引用私有泛型类的一个实例。对于特殊类型,该引用应该引用“特殊”类的一个实例。使用这种方法在执行一个方法时可能需要额外的虚拟分派级别。特殊的“,但将避免需要基于T
的其他运行时类型检查。
答案 4 :(得分:0)
我建议你的代码是这样的:
void Main()
{
B b = new B();
C c = new C(Guid.Empty);
b.Add<Guid>(Guid.Empty);
}
public class B
{
List<C> cs = new List<C>();
public void Add<T>(T v) { cs.Add(new C(v)); }
}
public class C<T>
{
public C(T c)
{
if(c is Guid)
{
Console.WriteLine("guid"); }
}else{
Console.WriteLine("object");
}
}
}
这个版本应该编译,注意你必须手动检查通用的类型,但
编辑:
由于列表实际上无法编译:
List<C> cs = new List<C>();
无法以类型安全的方式表示您的代码。您的选择是重新调整其工作方式或从object
构造函数中C
进行手动转换(或者可能看看dynamic
是否符合您的需求)