使用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> {}
这两个建议都将使我在创建派生类型时必须指定的类型参数的数量减至最少,而建议A将消除对大量冗余约束的需求。
那么,有没有理由C#无法/不应为这些建议之一提供支持? (或者偶然地我忽略了该语言的相关现有功能?)
最佳答案
这就是C#的基本方式。在哪些方面允许您对类型施加限制方面存在一些限制。
一般来说,where
子句的大多数选项与某些事情有关,添加此限制将使您可以对泛型类中的对象执行某些操作,该对象取决于所使用的泛型类型(例如:通过告诉它TID
是(IEquatable<TID>
),则可以使用TID,就好像它稍后是IEquatable一样。逻辑是,如果您不以任何方式依赖于该功能,那么实际上没有理由让您的班级需要该限制(尽管我承认出于简洁的考虑它可能是不错的选择)。
当涉及到所需的LogicBase<?>
时,就您现在可以使用此对象进行的工作提出了一个非常复杂的难题。当您的TLogic中定义了ServiceBase<TLogic>
的LogicBase<?>
时,实际上可以使用LogicBase的哪些功能?
如果您希望获得不依赖于其通用类型的某些功能子集,那么我要说的是,实际上您需要定义一个Interface
甚至只是一个abstract class
来定义该功能的子集ServiceBase<TLogic>
不依赖于数据类型TLogic
,并限制您的ServiceBase<TLogic>
的TLogic
为这种新类型。因为这实际上是您要应用程序为您推断的内容(一个表示LogicBase<?>
的组件的接口,该组件固有地不依赖于其通用类型)。
因此,虽然理论上编译器可以用这种方式解释这种情况,并且可以在不参考对象的数据类型的情况下对对象的约束和可用接口进行所需的强制实施,但在继承设置和我的继承的复杂性方面,这增加了可观的开销个人认为,简单地声明一个接口将是一种处理问题的更结构化的方式。