类签名中的泛型约束推理

时间:2012-07-02 07:14:54

标签: c# .net generic-constraints

在使用C#泛型约束时,我对某些要求感到沮丧,并且我想知道是否有解决我的问题的方法。如果没有,我想解释一下为什么事情不能像我希望的那样发挥作用。

此示例显示了我目前基本上要做的事情:

public abstract class EntityBase<TID>
    where TID : struct, IEquatable<TID>
{ }

public abstract class LogicBase<TEntity, TID>
    where TEntity : EntityBase<TID>
    where TID : struct, IEquatable<TID>
{ }

public abstract class ServiceBase<TLogic, TEntity, TID>
    where TLogic : LogicBase<TEntity, TID>
    where TEntity : EntityBase<TID>
    where TID : struct, IEquatable<TID>
{ }

// Concrete Examples
public class EgEntity : EntityBase<long> {}
public class EgLogic : LogicBase<EgEntity, long> {}
public class EgService : ServiceBase<EgLogic, EgEntity, long> {}

提案A 显示了我希望提供的内容(我认为没有理由不这样做):

public abstract class EntityBase<TID>
    where TID : struct, IEquatable<TID>
{ }

public abstract class LogicBase<TEntity>
    where TEntity : EntityBase<?>  // why not allow some kind of a "this could be whatever" syntax here ("?"), then infer the constraints on "?" based on the definition of EntityBase<>
{ }

public abstract class ServiceBase<TLogic>
    where TLogic : LogicBase<?>  // infer the constraints on "?" based on the definition of LogicBase<>
{ }

// Concrete Examples
public class EgEntity : EntityBase<long> {}
public class EgLogic : LogicBase<EgEntity> {}
public class EgService : ServiceBase<EgLogic> {}

提案B 显示了另一种可能的选择,但不像提案A那样具有吸引力:

public abstract class EntityBase<TID>
    where TID : struct, IEquatable<TID>
{ }

public abstract class LogicBase<TEntity>
    where TEntity : EntityBase<TID>  // introduce TID here to keep it out of class signature
    where TID : struct, IEquatable<TID>
{ }

public abstract class ServiceBase<TLogic>
    where TLogic : LogicBase<TEntity>  // introduce TEntity here
    where TEntity : EntityBase<TID>  // introduce TID here
    where TID : struct, IEquatable<TID>
{ }

// Concrete Examples
public class EgEntity : EntityBase<long> {}
public class EgLogic : LogicBase<EgEntity> {}
public class EgService : ServiceBase<EgLogic> {}

两个提案都会最小化我在创建派生类型时必须指定的类型参数的数量,而Proposal A将消除对一堆冗余约束的需要。

那么有没有理由认为C#不能/不应该为这些提案中的一个提供支持? (或者我有可能忽略了该语言的相关现有特征?)

2 个答案:

答案 0 :(得分:2)

这就是C#的基本方式。在允许你使用类型的限制方面有一些限制。

一般来说,where子句的大多数选项都与添加此限制的内容有关,它允许您对泛型类中的对象执行某些操作,这取决于所使用的泛型类型(EG:通过告知它TID是一个IEquatable<TID>,您可以使用TID,就像以后的IEquatable一样)。逻辑是,如果你不以任何方式依赖于那个功能,那么实际上没有理由你的班级需要限制(尽管我承认它对于清洁来说可能很好)。

当涉及到期望的LogicBase<?>时,一个非常复杂的谜题已被提出,正如你现在可以用这个对象做的那样。当你的TLogic定义为ServiceBase<TLogic> LogicBase<?>时,LogicBase的哪些功能实际上可以使用?

如果你想获得一些不依赖于知道它的泛型类型的功能的子集,那么我要说的是你需要定义一个Interface甚至只需一个{{1定义abstract class的功能,该功能不依赖于数据类型ServiceBase<TLogic>,并将TLogic的{​​{1}}限制为此新类型。因为这实际上是你要求应用程序为你推断的(一个代表ServiceBase<TLogic>的组件的接口,它们本身并不依赖于它的泛型类型)。

因此,在理论上,编译器可以通过这种方式解释这一点。在没有引用它的数据类型的情况下计算出对象的约束和可用接口的期望执行,它在继承设置的复杂性方面增加了显着的开销,我个人的想法是简单地声明一个接口会更多处理问题的结构化方式。

答案 1 :(得分:0)

为什么不定义IEntityBase接口并使LogicBase和ServiceBase仅依赖于此接口。这将使您能够摆脱TEntity类型参数。