通用类型约束检查

Mar*_*oxx 4 c# generics

是否有可能在调用这些方法之一时编写在编译或运行时(即以任何方式)失败的东西?

public static void Register<TInterface, TImplementation>()
    where TImplementation : class, TInterface
{
}
Run Code Online (Sandbox Code Playgroud)
public static void RegisterRestrictive<TInterface, TImplementation>()
    where TInterface : class
    where TImplementation : class, TInterface
{
}
Run Code Online (Sandbox Code Playgroud)

以下将通过两个例子:

public interface IInterface
{
}

public class Implementation : IInterface
{
}


public void Test()
{
    Register<IInterface, Implementation>();
    RegisterRestrictive<IInterface, Implementation>();
}
Run Code Online (Sandbox Code Playgroud)

我不认为是,因为你不能扩展结构?

因为这个https://github.com/aspnet/DependencyInjection/pull/624

Eri*_*ert 7

问题是,据我了解:

class C<T, U> where T : class, U where U : class { }
class D<T, U> where T : class, U { }
Run Code Online (Sandbox Code Playgroud)

是否有合法的建筑D是不合法的C

如果不UT封闭的类型.也就是说,其中没有类型参数的类型.正如Jon Hanna的回答指出的那样,开放式可能会导致问题:

class N<T, U> where T : class, U { C<T, U> c; D<T, U> d; }
Run Code Online (Sandbox Code Playgroud)

D没有达到约束,所以这种结构是违法的.

对于封闭类型的类型参数,我们可以推断如下:

C,U约束需要作为引用类型.

D,T可以是类,接口,委托或数组.在每种情况下,U必须是相同的T,或者基类T,或者某些东西T可以转换为via(可能是变体)隐式引用转换.无论如何,U都是参考类型.

请注意,C#编译器,CLR验证器和JIT编译器都不需要推断它U总是一个引用类型!例如,在这种情况下,C#编译器会生成不必要的装箱指令U,即使您和我知道这U不会是正在构建的值类型.

这可能导致a U盒装然后立即取消装箱的情况,以及我最后一次检查 - 这是,呃,十年前 - 抖动没有为该场景生成最佳代码.毫无疑问,自从我上次检查以来,抖动已被重写了一次或多次,你可能不应该接受我的话.

这里的好习惯是将约束放在那里并拼出来.


一些有趣的相关事实:

你可以通过拉扯类似的恶作剧来进入类似的情况

class B<T> { public virtual void M<U>(U u) where U : T {} }
class D : B<int> { public override void M<U>(U u) { } }
Run Code Online (Sandbox Code Playgroud)

请注意,C#不允许您重新声明约束,现在就是这样where U : int.但是你不能M用int之外的任何东西进行泛型构造.

这可能导致IL生成中的一些真正奇怪的场景,因为CLR具有记录不良的规则,因此它不允许类型参数的"已知为参考类型"的性能在虚拟覆盖.我重做了这些方法的代码生成,试图得到的东西,将汇编,通过验证,并有效地JIT 几个放弃并回到任何C#2之前做过倍.