2016-10-07 49 views
3
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的< ICardEntity>,而不是IGrouping < <匿名類型:短H>,卡>。爲什麼約束「T:ICardEntity」不起作用?C#在擴展忽略與泛型類型約束

錯誤消息:

類型「System.Linq.IGrouping < <匿名類型:短H>, Nucleo.Tests.Models.Card>不能被用作類型參數 'T'泛型類型或方法'MyExtensions.ToList(IQueryable)'。 不是從 'System.Linq.IGrouping <, Nucleo.Tests.Models.Card>'到'ICardEntity'的隱式參考轉換。

+1

泛型類型約束在重載解析中不起作用。 –

+0

順便說一句,沒有必要創建一個匿名類型:'GroupBy(t => new {Type = t.Type})''。爲什麼不'GroupBy(t => t.Type)'? –

回答

3

在C#中,沒有超過通用約束超載。您可以重載參數數量和參數類型,但不能重載返回值,超過通用約束等。就這些了。

在重載解析期間,編譯器會簡單地使用ToList(),因爲方法的名稱以及參數的數目和類型相匹配。它後來驗證了通用約束,但是在那一點上它已經是一個錯誤,而不是一個重載 - 解析 - 候選 - 退出。我不知道如何以更可讀的方式表達它。我希望你明白。

後以爲 -

在情況下,如果你是使用C++和它的模板,那麼它是不是C++並沒有SFINAE這隻會讓儘可能你想的那樣發揮作用。所有'重載決議'和'方法匹配/查找/等'必須遵循CLS/CLR的規則,所有語言如VB,C#,F3等的整個平臺。CLS/CLR定義如何查找方法,如何解決超載候選項等。而且,可悲的是,通過泛型類型參數約束進行重載,它不包含在規範中。 IIRC將其添加到規格中會在整個平臺上產生幾個問題(IIRC,主要是型號爲&方法解析性能),所以它沒有被添加..但這就是我所記得的。重要的部分是,它不在語言和運行時平臺規範中。

+2

顯然它在VB中的工作方式不同,因爲這是作爲(長)評論的一部分提及的[Eric Lippert解釋這一點](https://blogs.msdn.microsoft.com/ericlippert/2009/12/10/constraints-are不是部分簽名/),所以它不一定是CLS/CLR的東西。 –

+0

@Damien_The_Unbeliever:謝謝你提醒我關於VB的行爲。那就對了。它以不同的方式在那裏工作。看起來這個具體的東西更多的是關於編譯器的設計。 - 但是 - 我相信我對CLS/CLR的評論也是如此。如果我沒有記錯的話,如果我調用反射(非常接近clr :))Type.GetMethod()並將它傳遞給一個方法名和帶有參數規範的Type [],它將不會重載泛型約束。如果你確定我錯了,請告訴我,我會刪除那段文字,晚上我會自己檢查一下。 – quetzalcoatl