2009-09-04 47 views
3

在代碼中只存在一種實現IResourceConverter的類型。這就是以下兩條linq聲明所要查找的內容。前者找不到它。後者的確如此。但是它們都是等效的語法(或者至少應該是!)。linq拼圖...等效的語法...不等效的結果!

LINQ的聲明1:

List<Type> toInstantiate = AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(assembly => assembly.GetTypes()) 
    .Where(type => typeof(IResourceConverter).IsAssignableFrom(type) 
     && type != typeof(IResourceConverter)) 
    .ToList(); 

這將返回0的結果。

LINQ的聲明2:

我已經離開了LINQ除了where子句中,這是我爆發了,並用foreach循環

List<Type> toInstantiate = new List<Type>();    
List<Type> allTypes = AppDomain.CurrentDomain.GetAssemblies() 
    .SelectMany(assembly => assembly.GetTypes()) 
    .ToList(); 

foreach (Type t in allTypes) 
{ 
    if (typeof(IResourceConverter).IsAssignableFrom(t) 
     && t != typeof(IResourceConverter)) 
    toInstantiate.Add(t); 
} 

在這種情況下做了相當的toInstantiate有1個結果不變......正是我所期望的。

這種奇怪的行爲的任何解釋?

+0

我假設toInstantiate中的1結果確實實現了IResourceConverter?您可能想要添加/澄清一些事實......在上下文不明確的情況下。 – jrista 2009-09-04 18:53:45

+0

對不起,jrista。是的,存在一種實現IResourceConverter的類型。這就是linq聲明正在尋找的。前者找不到它。後者的確如此。但是它們似乎都是等效的語法。 – Daniel 2009-09-04 18:56:51

+0

我注意到這也發生,如果你仍然使用linq,但保持獨立的地方,並從allTypes查詢它。 – 2009-09-04 18:58:26

回答

2

運行以下程序,並使用diff工具比較a.txtb.txt文件。

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Collections; 
using System.IO; 
using System.Reflection; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      var x = Foo().OrderBy(t => t.FullName).Select(t => t.FullName); 
      var y = Bar().OrderBy(t => t.FullName).Select(t => t.FullName); 

      File.WriteAllLines("a.txt", x.ToArray()); 
      File.WriteAllLines("b.txt", y.ToArray()); 
      Console.ReadKey(); 
     } 

     private static List<Assembly> Foo() 
     { 
      List<Type> toInstantiate = AppDomain.CurrentDomain 
       .GetAssemblies().SelectMany(assembly => assembly.GetTypes()) 
       .ToList(); 

      return toInstantiate.Select(t => t.Assembly).Distinct().ToList(); 
     } 

     private static List<Assembly> Bar() 
     { 
      List<Type> toInstantiate = new List<Type>(); 
      List<Type> allTypes = AppDomain.CurrentDomain 
       .GetAssemblies().SelectMany(assembly => assembly.GetTypes()) 
       .ToList(); 

      foreach (Type t in allTypes) 
      { 
       toInstantiate.Add(t); 
      } 

      return toInstantiate.Select(t => t.Assembly).Distinct().ToList(); 
     } 
    } 
} 

我注意到兩部分代碼可以看到的程序集有很大差異。也就是說,第二個函數Bar可以看到基於linq的程序集不能。

更有趣的是,如果我反轉執行順序,現在Foo可以看到另一個不能執行的程序集,也就是完全相反。

最後,如果我運行第一查詢的兩倍,輸出的是相同的,因此:

 
    Foo, Foo, Bar 
    Foo, Bar, Foo 
    Bar, Bar, Foo 
    Bar, Foo, Bar 

所有產生相同的結果。

所以我唯一的假設是某些程序集正在加載一個查詢,其他查詢不會導致加載。

+0

非常好的東西 - 感謝這個守護者的所有工作。 – Daniel 2009-09-04 21:03:41

0

我不是一個真正的LINQ專家,但我可以冒險猜測;這看起來類似於我在Hibernate(Java ORM工具)中遇到的一個問題:

在Hibernate中,Collection屬性可以設置爲初始化懶惰。當屬性未初始化時,Hibernate使用字節碼工具生成代理,方法是繼承Parent並添加自己的行爲來執行延遲加載操作。如果我有類:

class Test { 
Collection<Parent> getEntities() //lazy 
} 

class Parent extends Child { 
} 

class Child { 
} 

我可以調用getEntities()來返回父對象的集合。由於我的getEntities被標記爲懶惰,所以我找回的對象是Parent自動生成的子類。即使集合中的其中一個項目表示爲Child,但「myEntity instanceof Child」之類的檢查也不起作用,因爲實際對象只是Child的代理,而不是真正的Child對象。

我知道LINQ是一種查詢機制,可用於數據訪問或對象。在你的情況下,也許你的where子句中的「type」對象是某種類型的實際Type對象的查詢代理,類似於上面描述的情況,所以isAssignable()確定代理對象沒有實現IResourceConverter?

0

確保低於實際的表達式的值爲true(即把它拿出來的LINQ語句,並直接針對該類型本身對其進行評估):

bool doesImplIface = typeof(IResourceConverter).IsAssignableFrom(type) && type != typeof(IResourceConverter); 

的唯一原因Where子句會過濾結果是因爲表達式不是真正的評估。運行上面的代碼行應清楚地表明您嘗試執行的表達式是否按照您認爲它的方式進行評估。如果沒有,調整它直到你得到適當的行爲,並把新的表達式放在LINQ語句中。

+0

事情是,兩個表達式是相同的,唯一的區別是一個來自列表,另一個是懶惰評估的枚舉。 – 2009-09-04 19:04:09

+0

我期望懶惰的枚舉器和列表行爲相同。如果您以部分方式評估或作爲綜合整體評估應該沒有關係......結果應該是一樣的。至少,據我所知,IEnumerable 和LINQ它應該。 – jrista 2009-09-04 20:45:42

2

這也似乎產生正確的結果,但我不知道爲什麼。

List<Type> allTypes = AppDomain.CurrentDomain 
    .GetAssemblies().SelectMany(assembly => assembly.GetTypes()) 
    .ToList(); 

List<Type> toInstantiate = allTypes 
    .Where(type => typeof(IList).IsAssignableFrom(type) && type != typeof(IList)) 
    .ToList(); 

foreach,這個查詢和原始查詢之間唯一的區別是原始查詢是懶惰評估。爲什麼這會有所作爲,因爲類型是靜態的,我不確定。

+1

區別在於,通過這種方式,您可以首先加載所有相關程序集,然後檢查IsAssignableFrom。當只有一個LINQ查詢時,在檢查的那一刻,加載的唯一依賴程序集就是我們已經迭代的程序集的那些。在這裏,「依賴」是指包含基類型/實現的類型接口的程序集,我們正在迭代它們。即便如此,我仍然不明白爲什麼這會有所作爲。 – 2009-09-04 19:23:53