7

When I use a method with a generic parameter to create another object, the generic object isn't selecting the most specific constructor. That sounds confusing, so here's some sample code to demonstrate what I mean...

Can anyone explain why the output of this program is:

guid    <-- easy - no problem here
object  <-- WHY?  This should also be "guid"?!

...and how to make the generic Add<T> function call the correct constructor of C?? Here's the code:

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"); }
}
4

5 回答 5

10

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

于 2013-07-16T18:50:27.707 回答
3

C# 中的泛型与 C++ 模板的工作方式不同 - 它们不会在编译时根据使用情况进行扩展。创建了一个方法,并且从其中调用的方法是静态解析的。

因此, within Add,v可以是任何类型,所以唯一知道的就是继承自object所以object构造函数 forC是唯一的候选者。

要获得您想要的行为,您必须添加另一个重载,Add例如

public void Add(Guid g) { cs.Add(new C(g)); }
于 2013-07-16T18:49:38.967 回答
0

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

public void Add<T>(T v) where T : Guid {
  cs.Add(new C(v));
}

当然,现在您只能使用 Guid 或 Guid 的扩展来调用 Add。回想起来有点无意义。我认为对于您要查找的内容,您会进行运行时检查。if (v is Guid)我想......对不起,我忘记了 C# 类型检查运算符是什么。

于 2013-07-16T18:57:35.160 回答
0

如果定义了一个类Foo<T> where T:SomeClass,所有的方法绑定和操作符重载都将像以前一样T执行SomeClass;在没有约束的情况下,它们通常会像以前一样执行ObjectT在评估绑定和重载时,没有可以考虑类型的机制。

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

于 2013-07-16T19:07:36.420 回答
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>();

不可能以类型安全的方式表示您的代码。您的选择是重组其工作方式或从构造函数中进行手动转换objectC或可能查看是否dynamic符合您的需求)

于 2013-07-16T19:11:40.843 回答