为什么使用继承类型没有检测到泛型类的类约束?

时间:2017-07-04 22:39:46

标签: c# generics inheritance

这个问题难以理解,我希望以下代码片段能说清楚:

public class DemoClass<TBase> where TBase : class
{
    public void DemoMethod<T>(T target) where T : TBase
    {
        //The following line causes a design-time error: Type argument 'T' does not satisfy the 'Class' constraint for type parameter 'T'.
        WeakReference<T> demoRef = new WeakReference<T>(target);
    }
}

WeakReference需要一个满足T约束的类型class。到目前为止,这么好,但是......

为什么编译器无法检测T实际上是什么,因为(实际上)T : TBase : class

2 个答案:

答案 0 :(得分:5)

  

为什么编译器不能检测到T实际上是这样,因为(实际上)T:TBase:class?

因为那根本不是真的。除Pokeanswer中指出的内容之外,由于所有值类型都继承自 object,这也是非法的:

 var dc = new DemoClass<object>();
 dc.DemoMethod(1); //woops, just attempted to create a WeakReference<int>

当涉及到价值类型时,您的推理就会崩溃。做作?是的,但完全合法,因此编译器没有选择权并且必须将您的代码视为非法。

更新

在下面的Jon Hana的评论中说明T上面的代码并非真正int,其object1被隐式包装,这绝对不是真的。请考虑DemoMethod的以下变体:

public T DemoMethod<T>(T target) where T : TBase
{
    return target;
}

以下代码:

var dc = new DemoClass<object>();
var i = dc.DemoMethod(1);

iint,而不是object。此外,以下将正确执行:

long i = dc.DemoMethod(1);

这也证明T不能是盒装int,因为隐式转换会在运行时失败;除了类型本身之外,您无法将值类型取消装箱。

当然,你总是可以明确地设置T,这也可以很好地编译:

dc.DemoMethod<int>(1);

答案 1 :(得分:3)

让我们检查the documentation INT_MAX实际含义:

  

<强> T : class

     

type参数必须是引用类型;这也适用于任何类,接口,委托或数组类型。

不幸的是,如果where T : class是例如接口,则已经满足。因此,您可以构建一个简单的示例,您可以看到传递地应用T将不起作用:

T : class

如果您现在创建public interface ITest { } public struct Test : ITest { } ,则您满足类型约束,因为此处DemoClass<ITest>是“类”。但是当您调用方法ITest时,虽然DemoMethod<Test>确实继承了T,但您没有Test的引用类型。

通常,那些特殊的泛型类型约束遵循继承规则。这就是为什么它们是单独定义的,并且尚未由类型系统建立。它们作为一种特殊的语法存在,因为类型系统无法表达其他的构造。