2012-11-24 74 views
3

我有10個表格以不同的類型呈現給LINQ,但共享完全相同的屬性。當我試圖在它們上面運行的聯合,那麼編譯器告訴我說:LINQ聯合不同類型 - 動態轉換爲接口?

"Argument 2: cannot convert from 'System.Collections.IEnumerable' to 'System.Collections.Generic.IEnumerable<LINQPad.User.TelJun2011>'" 

的代碼看起來像這樣

var jul = (from n in TelJul2011s select n); 

var jun = (from p in TelJun2011s select p); 

jun.Union(jul).Dump(); 

我做我的研究和理解,工會不能在不同的執行類型,我也明白,工會可以在匿名類型上執行,如果他們共享相同的屬性。這個選項對我來說不起作用,因爲我需要所有表中的所有屬性,並且不希望爲每個變量輸入相同的匿名類型10次。我希望編譯器根據所有屬性都相同的事實推斷它們都是相同的類型。

我已經嘗試使用AsQueryable()類型函數和「as」關鍵字轉換爲IEnumberable,Iqueryable,Datatable等。這似乎沒有爲我做詭計。

我想知道是否有某種方式通過動態轉換爲父類型來實現此目的。我無法編輯類的初始聲明,因此無法在它們上實現通用接口來投射。但是有什麼方法可以在使用它們時將類型轉換爲通用接口,而不需要編寫從每種類型到父接口的轉換?

感謝您的任何建議!

+1

據我所知,你已經是唯一的選擇......並說你不能使用它們...所以基本上這些限制的答案是否定的。 – Yahia

+0

是的,就是這樣。正確的解決辦法是有一個特定的非平板電腦類型(不是匿名),並選擇到這一個。來源原因顯然是破壞了數據庫設計。 – TomTom

+0

不破壞數據庫設計。我正在評估使用linq作爲臨時分析解決方案來查詢多個來源的潛力。不同來源的聯盟是通常用於分析的基本SQL功能,即使我同意在生產或實際應用中這種做法很差。 – analystic

回答

7

Union的結果將是IEnumerable<Xxx>,但在這種情況下,您必須指定什麼Xxx是。如果類型TelJun2011TelJul2011結構(值類型),你可以利用的IEnumerable<out T>協方差這樣的:

jun.Union<object>(jul) 

這工作,因爲TelJun2011TelJul2011都是object,然後通過協方差IEnumerable<TelJun2011>IEnumerable<TelJul2011>都是IEnumerable<object>

當然,object不具備TelJun2011TelJul2011共有的所有屬性。它會更好,如果這兩種類型有一個更有用的公共基類或implemened一個通用的接口,因爲這樣你可能會說例如爲:

jun.Union<ITelMonth>(jul) 

其中ITelMonth均包含所需的公共屬性某種類型。

+0

這個答案太神奇了......在閱讀本書之前,我並沒有真正閱讀過協變,因爲我不知道它是如此強大的新語言功能。再次感謝您分享您的知識Jeppe! – analystic

0

「問題」是C#的靜態類型系統不允許你試圖實現的目標。在將它們合併之前,您必須將對象轉換爲基本類型。這意味着如果兩者只有System.Object的共同點,則必須將其投射到object或(對於.net 4.0)至dynamic

後者將強制動態類型檢查:

class A 
{ 
    public int Integer { get; set; } 
} 

class B 
{ 
    public int Integer { get; set; } 
} 

class Program 
{ 
    public static void main(string[] args) 
    { 
     var a = new[] { new A { Integer = 5 }, new A { Integer = 6 }, new A { Integer = 7 } }; 
     var b = new[] { new B { Integer = 1 }, new B { Integer = 2 }, new B { Integer = 3 } }; 

     var u = a.Cast<dynamic>().Union(b).ToArray(); 

     var i1 = u[0].Integer; 
     var i2 = u[1].Integer; 
     var i3 = u[2].Integer; 
     var i4 = u[3].Integer; 
     var i5 = u[4].Integer; 
     var i6 = u[5].Integer; 
    } 
} 

在我看來,這不是一個理想的解決方案,但是這可能會幫助您。

+0

我不認爲你需要'Cast'方法。 「IEnumerable 」的協變不應該允許你只寫:'var u = a.Union (b);' –

+0

是的。謝謝傑普。 – Peit

0

如果您使用的EntityFramework(或任何其他框架,我猜),類自動生成的標記爲partial,如下列:

/Project/Data/TelJun2011.cs

namespace Project.Data 
{ 
    using System; 
    using System.Collections.Generic; 

    public partial class TelJun2011 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
     public string Description { get; set; } 
    } 
} 

/Project/Data/TelJul2011.cs

namespace Project.Data 
{ 
    using System; 
    using System.Collections.Generic; 

    public partial class TelJul2011 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
     public string Description { get; set; } 
    } 
} 

partial表示您可以爲同一個類創建另一個文件。生成的類沒有實現一個接口,但很容易使,那麼你可以實現你這樣的自定義接口:

/Project/Data/ITelMonth.cs

namespace Project.Data 
{ 
    public interface ITelMonth 
    { 
     int Id { get; set; } 
     string Name { get; set; } 
     string Description { get; set; } 
    } 
} 

/項目/數據/部分/ TelJun2011.cs

namespace Project.Data 
{ 
    public partial class TelJun2011 : ITelMonth { } 
} 

/Project/Data/Partial/TelJul2011.cs

namespace Project.Data 
{ 
    public partial class TelJul2011 : ITelMonth { } 
} 

,並具有正確定義的接口,那麼我們就可以簡單地這樣做:

var jul = (from n in TelJul2011s select (ITelMonth)n); 

var jun = (from p in TelJun2011s select (ITelMonth)p); 

var bimester = jun.Union(jul); 

你甚至可以訪問像這樣的公共屬性:

foreach (var e in bimester) 
{ 
    e.Id.Dump(); 
}