【问题标题】:c# constraints ignored in extension with generic typec# 在泛型类型的扩展中忽略约束
【发布时间】:2016-10-07 08:56:17
【问题描述】:
public interface ICardEntity { ... }

public class Card : ICardEntity { ... }

public static class MyExtensions
{
    public static List<T> ToList<T>(this IQueryable<T> query) where T : ICardEntity
    { ... }
}

// DbContext
public class ApplicationDbContext : DbContext
{
      public DbSet<Card> Cards { get; set; }
}

.....

var list = dbContext.Cards
     .ToList(); // -- OK, my extension is called
var list2 = dbContext.Cards
     .GroupBy(t => new { Type = t.Type})
     .ToList(); // -- Compile error. Why not System.Linq.Enumerable.ToList() is called?

我希望我的 ToList 只为 IQueryable 调用,而不是为 IGrouping, Card> 调用。为什么约束“where T : ICardEntity”不起作用?

错误信息:

类型'System.Linq.IGrouping, Nucleo.Tests.Models.Card>' 不能用作类型参数 'T' 泛型类型或方法“MyExtensions.ToList(IQueryable)”。那里 不是从隐式引用转换 'System.Linq.IGrouping' 到 'ICardEntity'。

【问题讨论】:

  • 泛型类型约束不参与重载决议。
  • 顺便说一句,不需要创建匿名类型:GroupBy(t =&gt; new { Type = t.Type })。为什么不GroupBy(t =&gt; t.Type)

标签: c# entity-framework linq generics


【解决方案1】:

在 C# 中,没有泛型约束的重载。您可以重载参数计数和参数类型,但不能重载返回值、泛型约束等。仅此而已。

在重载解析期间,编译器只需使用您的 ToList(),因为方法的名称、参数的数量和类型匹配。之后它会验证通用约束,但此时它已经是一个错误,而不是重载解决候选退出。我不知道如何以更易读的方式表达它。我希望你能明白。

想了想——

如果您习惯了 C++ 及其模板,那么它就不是 C++,也没有 SFINAE 可以让它像您想象的那样工作。

所有“重载解析”和“方法匹配/查找/等”都必须遵循 CLS/CLR 的规则,整个平台适用于 VB、C#、F3 等所有语言。正是 CLS/CLR定义如何查找方法、如何解决重载候选者等。而且,可悲的是,对泛型类型参数约束的重载并未包含在规范中。 IIRC 将其添加到规范中会在整个平台中产生几个问题(主要是 IIRC,类型和方法解析性能),因此没有添加它..但这就是我记得的。重要的是,它不在语言和运行时平台规范中。

【讨论】:

  • 显然它在 VB 中的工作方式不同,因为这是作为 Eric Lippert explains this 的(长)cmets 的一部分提到的,所以它不一定是 CLS/CLR 的事情。
  • @Damien_The_Unbeliever:感谢您提醒我有关 VB 的行为。这是正确的。它以不同的方式在那里工作。似乎这个具体的事情更多的是关于编译器设计。 - 但是 - 我相信我所说的关于 CLS/CLR 的内容也是正确的。如果我没记错的话,如果我调用反射(非常接近 clr :))Type.GetMethod() 并将其传递给方法名称和带有参数规范的 Type[],它不会超过通用约束。如果您确定我对此有误,请告诉我,我将删除那段文字,并在晚上晚些时候亲自检查。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-07
  • 2017-01-21
  • 1970-01-01
相关资源
最近更新 更多