C# 通用方法是';选择最具体的构造函数签名?

C# 通用方法是';选择最具体的构造函数签名?,c#,generics,C#,Generics,当我使用带有泛型参数的方法创建另一个对象时,泛型对象没有选择最具体的构造函数。这听起来很混乱,所以这里有一些示例代码来演示我的意思 有人能解释为什么这个程序的输出是: GuID C++中的泛型与C++模板不一样,它们在编译时不会根据使用情况而扩展。创建一个方法,并静态解析从其中调用的方法 因此,在Add中,v可以是任何类型,因此唯一已知的是它继承自object,因此C的object构造函数是唯一的候选对象 要获得您想要的行为,您必须添加另一个超负荷的add,例如 public void Add

当我使用带有泛型参数的方法创建另一个对象时,泛型对象没有选择最具体的构造函数。这听起来很混乱,所以这里有一些示例代码来演示我的意思

有人能解释为什么这个程序的输出是:

<代码> GuID

C++中的泛型与C++模板不一样,它们在编译时不会根据使用情况而扩展。创建一个方法,并静态解析从其中调用的方法

因此,在
Add
中,
v
可以是任何类型,因此唯一已知的是它继承自
object
,因此
C
object
构造函数是唯一的候选对象

要获得您想要的行为,您必须添加另一个超负荷的
add
,例如

public void Add(Guid g) { cs.Add(new C(g)); }

重载解析在编译时完成,而不是在运行时完成。因此,当您从
Add
方法调用
newc(v)
时,编译器不知道
t
实际上是
Guid
,因此它使用唯一保证兼容的重载,即
public(object C)

Lee和Thomas的答案是正确的。为了增加它们,我相信向泛型添加约束可能会使它能够选择最具体的一个。重写Add方法:

public void Add<T>(T v) where T : Guid {
  cs.Add(new C(v));
}
public void Add(tv)其中T:Guid{
增加(新的C(v));
}

当然,现在只能使用Guid或Guid的扩展名调用Add。回想起来有点毫无意义。我想对于您要查找的内容,您应该进行运行时检查
如果(v是Guid)
我想……对不起,我忘记了C#type check操作符是什么了。

如果定义一个类
Foo,其中T:SomeClass
,所有方法绑定和操作符重载都会像
T
was
SomeClass
一样执行;在没有约束的情况下,它们通常会像对象一样执行。在评估绑定和重载时,不存在可以考虑
T
类型的机制。


如果需要一些特殊行为的类型,可以使用一个或多个方法定义一个私有泛型接口,以对类型
T
执行操作,这是一个以“通常”方式实现该接口的私有泛型类,以及私有的非泛型类,它们以适合于特定应用程序的方式实现接口“特殊类型。私有泛型类应该持有对接口类型的静态引用。对于非特殊类
T
,该引用应引用私有泛型类的实例。对于特殊类型,该引用应引用“特殊”类的实例。在执行可能是“特殊”的方法时,使用此方法将需要额外级别的虚拟调度,但将避免基于
T

的其他运行时类型检查,我建议您将代码设置为如下:

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");
        }
    }
}
void Main()
{
B=新的B();
C=新的C(Guid.Empty);
b、 添加(Guid.Empty);
}
公共B级
{
List cs=新列表();
公共无效添加(tv){cs.Add(新的C(v));}
}
公共C类
{
公共电话(TC)
{ 
if(c是Guid)
{
Console.WriteLine(“guid”);}
}否则{
控制台。写入线(“对象”);
}
}
}
这个版本应该可以编译,注意您必须手动检查泛型的类型

编辑:

由于列表的原因,这实际上无法编译:

List<C> cs = new List<C>();
List cs=new List();

不可能以类型安全的方式表示代码。您的选择是重新构造其工作方式,或者从
C
构造函数中的
object
进行手动强制转换(或者可能查看
dynamic
是否符合您的需要)

是否按类进行?因为我看了这个,我知道
T
是一个
Guid
,因为
b.Add(…)
。但是如果是基于每个类,那么我可以理解,因为
B
类可能在预编译的DLL中。这听起来像是我理解吗?如果这是C++模板,那么在编译时生成特定类型的代码会调用更具体的重写吗?@ MichaelBray,是的,实际上,对于泛型方法,它是按每个方法进行的。编译
Add
方法时,编译器不会查看它的调用方式,只会查看方法本身。@thomasleveque:aha。。。我通过删除重载构造函数
C(object C)
对此进行了测试,果然,它不再编译,抱怨它
无法从't'转换为'System.Guid'
。这是有道理的。这里的教训是:C++不是C++,泛型不是模板。泛型是类型系统的一部分,类型系统是一个逻辑框架,编译器在其中对源代码的含义进行逻辑推断。模板更像是一个词汇系统;你可以将模板实例化想象为简单地替换文本,然后从头开始分析新文本。这甚至不需要编译-Guid不是有效的泛型类型约束。是的,我不认为我意识到Guid是一个结构,而不是一个类。强制特定结构的泛型定义将…根本不是泛型。对不起,那没用。