将接口上的泛型参数限制为子类

Posted

技术标签:

【中文标题】将接口上的泛型参数限制为子类【英文标题】:Restrict generic parameter on interface to subclass 【发布时间】:2012-07-19 04:04:39 【问题描述】:

以下是人为的,但请耐心等待:

interface Clonable<TSubClass>

    TSubClass Clone();

如何将 TSubClass 限制为实现类型?

即只让实现者这样做:

class Dog : Clonable<Dog>

    Dog Clone() 
    
        ....
    

不是这个:

class BadDog : Clonable<Rabbit>

    Rabbit Clone()
    
        ....
    

【问题讨论】:

【参考方案1】:

你 can't 只能通过约定和文档来强制执行......

对我来说,约定是使用TSelf 之类的东西。

interface ICloneable<TSelf> where TSelf : ICloneable<TSelf>  ... 

还要注意,任何实现或继承此接口的非具体构造都应通过...传递约束。

[Serializable]
abstract class SerializableCloneable<TSelf> : ICloneable<TSelf> 
  where TSelf : SerializableCloneable<TSelf>  ... 

注意:我已经在NRoles 中实现了这个检查,使用调用你的self-type parameter S 的约定。

【讨论】:

您也可以在运行时强制执行它。请参阅 Ben Voigt 的回答。 @usr:这行不通,只有将静态构造函数添加到具体类本身,这将是完全多余的。【参考方案2】:

您无法在编译时强制执行此操作,因为 .NET 泛型没有模板特化或鸭子类型。

但是,您可以包含一个静态构造函数(类型初始化程序),该构造函数使用反射在加载时断言关系。好的,C# 不允许您在接口上放置静态构造函数(即使 .NET 确实允许),因此您需要使用模块初始化程序或您自己调用的函数。此外,您还需要搜索实现该接口的类型,包括尚未加载的类型(您可以订阅Assembly.Load 事件,以便在将来加载类型时收到通知)。

【讨论】:

静态 ctor 的想法很棒。 -1 静态构造函数必须在具体类本身中定义,这对于声明自类型参数本身来说是完全多余和不必要的。 @Jordão:这不是真的,反射可以发现所有从基类继承或实现接口的类。 @BenVoigt:好的,但这开始有点……麻烦。如果它们从您那里继承,您将不得不在所有加载的程序集中查找所有类。而且...您不能在 C# 中向接口添加静态构造函数。 @Jordão:我更多的是考虑基类。接口可以在 .NET 中具有类型初始化器(尽管 C# 编译器对此没有帮助),但它们不会在方便的时候运行。但是安排一段代码运行并不是那么困难。并且在加载新程序集时会运行一个事件。

以上是关于将接口上的泛型参数限制为子类的主要内容,如果未能解决你的问题,请参考以下文章

13.3.5 接口和委托的泛型可变性限制和说明

C# 泛型方法约束为继承自某类时,调用方法,传子类实参,为什么报错?应该怎么写

Java中的泛型理解

如何将方法的泛型类型限制为打字稿中的对象?

Java中的泛型

Java的泛型约束和限制