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

mik*_*ike 6 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

为什么编译器无法检测到T实际发生的情况,因为(实际上)T : TBase : class

InB*_*een 5

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

因为那根本不是真的.除了Poke在他的回答中指出的,这也是非法的,因为所有值类型都继承自 object:

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

当涉及到价值类型时,你的推理就会崩溃.做作?是的,但完全合法,所以编译器没有选择,必须考虑您的代码非法.

UPDATE

在上面的代码寻址低于乔恩·哈纳的评论T是不是真的int,它object1被隐式装箱,这是绝对不正确的.考虑以下变体DemoMethod:

public T DemoMethod<T>(T target) where T : TBase
{
    return target;
}
Run Code Online (Sandbox Code Playgroud)

以下代码:

var dc = new DemoClass<object>();
var i = dc.DemoMethod(1);
Run Code Online (Sandbox Code Playgroud)

i是的int,它不是object.此外,以下将正确执行:

long i = dc.DemoMethod(1);
Run Code Online (Sandbox Code Playgroud)

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

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

dc.DemoMethod<int>(1);
Run Code Online (Sandbox Code Playgroud)


pok*_*oke 4

让\xe2\x80\x99s 检查文档以了解T : class实际含义:

\n\n
\n

where T : class

\n\n

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

\n
\n\n

T不幸的是,如果是一个接口,这就已经满足了。因此,您可以构建一个简单的示例,在其中您可以看到T : class传递应用不起作用:

\n\n
public interface ITest { }\npublic struct Test : ITest { }\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果您现在创建一个DemoClass<ITest>,则满足类型约束,因为ITest这里是 \xe2\x80\x9cclass\xe2\x80\x9d 。但是,当您调用该方法时,尽管继承了,但DemoMethod<Test>您没有引用类型。TTestITest

\n\n

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

\n