简体   繁体   English

通用类型约束检查

[英]Generic type constraint checking

Is it possible to write something that will fail either at compile or runtime (ie by any means) on invocation of just one of these methods? 是否有可能在调用这些方法之一时编写在编译或运行时(即以任何方式)失败的东西?

public static void Register<TInterface, TImplementation>()
    where TImplementation : class, TInterface
{
}
public static void RegisterRestrictive<TInterface, TImplementation>()
    where TInterface : class
    where TImplementation : class, TInterface
{
}

The following will pass both for example: 以下将通过两个例子:

public interface IInterface
{
}

public class Implementation : IInterface
{
}


public void Test()
{
    Register<IInterface, Implementation>();
    RegisterRestrictive<IInterface, Implementation>();
}

I don't think it is, because you can't extend structs? 我不认为是,因为你不能扩展结构?

Asking because of this https://github.com/aspnet/DependencyInjection/pull/624 因为这个https://github.com/aspnet/DependencyInjection/pull/624

The question is, as I understand: 问题是,据我了解:

class C<T, U> where T : class, U where U : class { }
class D<T, U> where T : class, U { }

Is there a construction that is legal for D that is not legal for C ? 是否存在对D而言合法的结构对C不合法

Not if U and T are closed types . 如果UT封闭类型 ,则不是。 That is, types that have no type parameters in them. 也就是说,其中没有类型参数的类型。 As Jon Hanna's answer points out, an open type can cause a problem here: 正如Jon Hanna的回答指出的那样, 开放式可能会导致问题:

class N<T, U> where T : class, U { C<T, U> c; D<T, U> d; }

D 's constraints are not met, so this construction is illegal. D的限制没有得到满足,因此这种结构是非法的。

For type arguments which are closed types we can reason as follows: 对于封闭类型的类型参数,我们可以推断如下:

In C , U is required by constraint to be a reference type. C ,约束要求U是引用类型。

In D , T can be a class, interface, delegate or array. DT可以是类,接口,委托或数组。 In every case, U has to be identical to T , or a base class of T , or something T is convertible to via (possibly variant) implicit reference conversion. 在任何情况下, U必须是相同T ,或基类的T ,或东西T可转化为经由(可能变体)隐式引用转换。 No matter what, U is a reference type. 无论如何, U是参考类型。

Note though that neither the C# compiler, nor the CLR verifier, nor the JIT compiler are required to deduce that U is always going to be a reference type! 请注意,无论是C#编译器,CLR验证器还是JIT编译器都不需要推断出U始终是一个引用类型! The C# compiler can and will in this circumstance, for instance, generate unnecessary boxing instructions on usages of U , even though you and I know that U is not going to be a value type under construction. 例如,在这种情况下,C#编译器会对U用法生成不必要的装箱指令,即使您和我知道U不会是正在构造的值类型。

This can lead to situations where a U is boxed and then immediately unboxed, and the last time I checked -- which was, uh, ten years ago -- the jitter did not generate optimal code for that scenario. 这可能导致U被装箱然后立即取消装箱的情况,以及我最后一次检查 - 这是,呃,十年前 - 抖动没有为该场景生成最佳代码。 Since no doubt the jitter has been rewritten one or more times since I last checked, you should probably not take my word for it. 毫无疑问,自从我上次检查以来,抖动已被重写了一次或多次,你可能不应该接受我的话。

The good practice here is to put the constraint on there and spell it out. 这里的好习惯是将约束放在那里并拼出来。


Some fun related facts: 一些有趣的相关事实:

You can get into similar situations by pulling shenanigans like 你可以通过拉扯类似的恶作剧来进入类似的情况

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

Note that C# does NOT allow you to re-state the constraint, which is now where U : int . 请注意,C#不允许您重新声明约束,现在where U : int But you cannot do a generic construction of M with anything other than int. 但你不能用除int之外的任何东西做M的泛型构造。

This can lead to some truly bizarre scenarios in IL generation, because the CLR has a poorly-documented rule whereby it does not allow the "known-to-be-a-reference-type"-ness of a type parameter to change across a virtual override. 这可能导致IL生成中的一些真正奇怪的场景,因为CLR具有记录不良的规则,因此它不允许类型参数的“已知为参考类型”的性能在虚拟覆盖。 I redid the codegen for such methods to try to get something that would compile, pass the verifier, and jit efficiently several times before giving up and going back to whatever C# 2 did. 我为这些方法重新编写了codegen,试图在放弃并返回C#2所做的任何事情之前尝试获得可以编译,传递验证器和jit高效几次的内容。

Is it possible to write something that will fail either at compile or runtime (ie by any means) on invocation of just one of these methods? 是否有可能在调用这些方法之一时编写在编译或运行时(即以任何方式)失败的东西?

Yes, this fails at compile time: 是的,这在编译时失败了:

public static void CallThem<TA, TB>()
    where TB : class, TA
{
    Register<TA, TB>();         // Fine
    RegisterRestrictive<TA, TB>(); // CS0452
}

There's no pair of concrete types that match TInterface and TImplementation for only one of them, but the type-parameter types of a calling method certainly can, and type-parameter types are types we need to consider in designing APIs as well a concrete types. 没有一对与TInterfaceTImplementation仅匹配其中一个的具体类型,但调用方法的类型参数类型当然可以,而类型参数类型是我们在设计API时需要考虑的类型以及具体类型。

Constraints do not involve inference of other constraints. 约束不涉及其他约束的推断。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM