防止用于泛型参数的特定类型的层次结构

And*_*tan 5 .net c# architecture

我用一大堆有问题的背景开始这个问题; 架构背后的接口和基本原理.

然后我意识到 - "这就是这样 - 保持简单并达到目的".

所以这里.

我有一个这样的课:

public class AGenericType<T> : AGenericTypeBase
{
  T Value { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

当然,.Net允许我这样做:

AGenericType<AGenericType<int>> v;
Run Code Online (Sandbox Code Playgroud)

但是,在使用环境中AGenericType<T>,以同样的方式执行此操作是无稽之谈,因为这样做是无意义的:

Nullable<Nullable<double>> v;
Run Code Online (Sandbox Code Playgroud)

我希望能够做的是限制这种泛型类型,这样就不可能创建这样的实例,甚至在T派生它时也会声明对类型的引用AGenericTypeBase- 最好是在编译时.

现在一个有趣的事情是Nullable<T>我在这里给出的例子确实会产生编译器错误.但我无法看到如何Nullable<T>限制TNullable<T>类型 - 因为Nullable<T>是一个结构,并且我能找到的唯一通用约束(即使在IL中,它通常会产生编译器机密,就像那些带有委托的那些)where T:struct.所以我认为一个人必须是编译器黑客(编辑:请参阅下面的@Daniel Hilgarth的回答+评论,以便对此进行一些探索).编译器黑客当然不能重复!

至于我自己的方案中,IL和C#容许负断言约束是这样的:

public class AGenericType<T> : where !T:AGenericTypeBase
Run Code Online (Sandbox Code Playgroud)

(注意约束中的'!')

但我可以使用哪种替代方案?

我想到了两个:

1)在构造函数中生成的运行时异常AGenericType<T>:

public AGenericType(){
  if(typeof(AGenericBase).IsAssignableFrom(typeof(T)))
    throw new InvalidOperationException();
}
Run Code Online (Sandbox Code Playgroud)

但这并不能真正反映错误的本质 - 因为问题是泛型参数,因此是整个类型; 不只是那个例子.

2)相反,相同的运行时异常,但在静态初始化器中生成AGenericType<T>:

static AGenericType(){
  if(typeof(AGenericBase).IsAssignableFrom(typeof(T)))
    throw new InvalidOperationException();
}
Run Code Online (Sandbox Code Playgroud)

但后来我面临的问题是这个异常会被包含在内部TypeInitializationException并且可能会引起混淆(根据我的经验,实际上读取整个异常层次结构的开发人员实际上很薄).

对我而言,这是IL和C#中"否定断言"泛型约束的明显例子 - 但由于这不太可能发生,你会怎么做?

And*_*tan 1

最后,我在泛型类型的静态初始化程序中遇到了异常,因为它更清楚地表明调用者对该类型的使用存在问题,而不是传递给构造函数的运行时参数。该错误很容易被调用者修复,所以我认为这是最好的妥协。