是否可以编写一些在编译或运行时(即通过任何方式)调用这些方法之一时失败的东西?
public static void Register<TInterface, TImplementation>()
where TImplementation : class, TInterface
{
}
public static void RegisterRestrictive<TInterface, TImplementation>()
where TInterface : class
where TImplementation : class, TInterface
{
}
例如,以下将同时通过:
public interface IInterface
{
}
public class Implementation : IInterface
{
}
public void Test()
{
Register<IInterface, Implementation>();
RegisterRestrictive<IInterface, Implementation>();
}
我不认为是,因为你不能扩展结构?
最佳答案
问题是,据我了解:
class C<T, U> where T : class, U where U : class { }
class D<T, U> where T : class, U { }
是否存在对 D
合法但对 C
合法的构造?
如果 U
和 T
是封闭类型,则不是。也就是说,其中没有类型参数的类型。正如 Jon Hanna 的回答所指出的,开放类型 可能会在此处引起问题:
class N<T, U> where T : class, U { C<T, U> c; D<T, U> d; }
D
的约束不满足,所以这个构造是非法的。
对于封闭类型的类型参数,我们可以推理如下:
在 C
中,约束要求 U
是引用类型。
在D
中,T
可以是类、接口(interface)、委托(delegate)或数组。在任何情况下,U
都必须与 T
相同,或者是 T
的基类,或者 T
可转换为 via(可能是变体)隐式引用转换。不管怎样,U
都是引用类型。
请注意,无论是 C# 编译器、CLR 验证器还是 JIT 编译器都不需要推断 U
始终是引用类型!在这种情况下,C# 编译器可以而且将会在 U
的用法上生成不必要的装箱指令,即使你我都知道 U
不会成为一个构建中的值类型。
这可能会导致 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) { } }
请注意,C# 不允许您重新声明约束,现在是 where U : int
。但是你不能用除 int 以外的任何东西来做 M
的通用构造。
这可能会在 IL 生成中导致一些真正奇怪的场景,因为 CLR 有一个记录不完整的规则,它不允许类型参数的“已知为引用类型”-ness跨虚拟覆盖更改。我为此类方法重新编写了代码生成器,以尝试获得可以编译、通过验证程序并有效地 jit 几次 的东西,然后放弃并返回到 C# 2 所做的任何事情。
关于c# - 通用类型约束检查,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48471611/