【问题标题】:Generic constraint inference in class signature类签名中的通用约束推断
【发布时间】:2012-07-02 07:14:54
【问题描述】:

在使用 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# 不能/不应该为这些提案之一提供支持是否有原因? (或者我是否忽略了该语言的相关现有功能?)

【问题讨论】:

  • 我知道我以前读过类似的东西 - 虽然 Eric 的场景并没有 100% 符合问题,但他给出的地图的推理很好。
  • Eric Lippert 的文章非常棒,正是我需要阅读的内容,尽管我不确定它是否完全解决了提案 B。

标签: c# .net generic-constraints


【解决方案1】:

C# 基本上就是这样。在允许您对类型施加什么限制方面,存在一些限制。

一般来说,where 子句的大多数选项都与添加此限制将允许您对泛型类中的对象执行某些操作有关,这取决于所使用的泛型类型(例如:通过告诉它@ 987654322@ 是一个IEquatable&lt;TID&gt;,您可以使用 TID,就好像它稍后是一个 IEquatable 一样)。逻辑是,如果您不以任何方式依赖该功能,那么实际上您的课程没有理由需要限制(尽管我承认为了清洁起见它可能很好)。

当涉及到所需的LogicBase&lt;?&gt; 时,就您现在可以用这个对象做什么,提出了一个非常复杂的难题。当您有一个ServiceBase&lt;TLogic&gt; 并在其中定义了您的 TLogic 为LogicBase&lt;?&gt; 时,实际上可以使用 LogicBase 的哪些功能?

如果您希望获得一些不依赖于知道它是泛型类型的功能子集,那么我想说的是,您确实需要定义一个 Interface 甚至只是一个 abstract class 来定义ServiceBase&lt;TLogic&gt; 的功能不依赖于数据类型 TLogic 并限制您的 ServiceBase&lt;TLogic&gt;TLogic 属于这种新类型。因为这实际上是您要求应用程序为您推断的内容(表示 LogicBase&lt;?&gt; 的组件的接口,它们本身并不依赖于它的泛型类型)。

因此,虽然理论上编译器可以以这种方式解释这一点,并在不参考对象数据类型的情况下制定出所需的约束执行和对象的可用接口,但它在继承的复杂性方面增加了显着的开销设置和我个人的想法是,简单地声明一个接口将是一种更有条理的方式来处理这个问题。

【讨论】:

  • 我无法更好地解释它。
  • 我的意思是编译器可以合理地知道 ?通过查看 LogicBase 本身的签名在 LogicBase> 中。这并不是说 ?这意味着对类型参数没有约束,只是应该从包含的泛型类型推断出任何约束。基本上,当编译器可以合理地推断出完全相同的约束时,为什么还要在两个地方放置完全相同的约束呢?
【解决方案2】:

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

【讨论】:

  • 在我的具体情况下,界面无法满足我的需求。我真的想要泛型,因为我想处理基类中的实际类型。不过,我可能不完全理解你的建议。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-30
  • 1970-01-01
  • 2023-03-10
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多