2009-09-04 12 views
3

コードには、IResourceConverterを実装するタイプが1つだけあります。これは、次の2つのlinq文が探しているものです。前者はそれを見つけません。後者はそうです。しかし、それらはどちらも同等の構文です(または少なくとも!)。linq puzzle ...同等の構文...同等の結果はありません!

の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:私は勃発とtoInstantiateは1つの結果を有している。この場合、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); 
} 

と等価をしたwhere句を除いて完全なLINQを残している

...まさに私が期待していただろう。

この奇妙な動作についての説明はありますか?

+0

私は、toInstantiateの1つの結果が実際にIResourceConverterを実装していると仮定していますか?コンテキストをあいまいにしている瞬間に、いくつかの事実を追加/明確化したいかもしれません。 – jrista

+0

申し訳ありません、jrista。はい、IResourceConverterを実装する1つの型があります。これがlinq文が探しているものです。前者はそれを見つけません。後者はそうです。しかし、どちらも同等の構文であるようです。 – Daniel

+0

私はこれもlinqを使用しているが、どこに別の場所を保持し、allTypesからそれを照会する場合にも起こることに気付きました。 –

答えて

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(); 
     } 
    } 
} 

私は、コードの2枚が見ることができるアセンブリで大きな違いに気付いています。つまり、第2の関数Barは、linqベースのアセンブリでは不可能なアセンブリを見ることができます。

もっと興味深いことに、実行順序を逆にすると、今度はFooがアセンブリを見ることができます。つまり、正確な逆です。

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

すべてが同じ結果を生成:私は二回最初のクエリを実行する場合

最後には、出力はそう、同じです。

したがって、私の唯一の仮定は、あるアセンブリによっていくつかのアセンブリが読み込まれているために、他のクエリがロードされないということです。

+0

非常に良いもの - このキーパーのすべての作業に感謝します。 – Daniel

0

私は実際にはLINQのエキスパートではありませんが、私は推測の危険があります。これは、Hibernate(JavaのORMツール)で発生した問題と似ています。

Hibernateでは、Collectionプロパティをlazilyに初期化するように設定できます。プロパティが初期化されていない場合、Hibernateは、Parentをサブクラス化し、独自の動作を追加してレイジーローディング操作を行うことによって、バイトコードインストルメンテーションを使用してプロキシを生成します。

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

class Parent extends Child { 
} 

class Child { 
} 

私はgetEntities()を呼び出してParentオブジェクトのコレクションを返すことができます。私のgetEntitiesは遅延表示されているので、戻ってくるオブジェクトは自動的にParentのサブクラスとして生成されます。コレクション内の項目の1つが子を表していても、実際のオブジェクトは実際の子オブジェクトではなく子へのプロキシであるため、「myEntity instanceof Child」のようなチェックは機能しません。

私はLINQがデータアクセスやオブジェクトに使用できるクエリメカニズムであることを理解しています。あなたのケースでは、where句の "type"オブジェクトは、上記のケースと同様に、実際のTypeオブジェクトに対して何らかの種類のプロキシを照会するものなので、isAssignable()はプロキシオブジェクトがIResourceConverterを実装していないと判断します。

0

(つまり、LINQ文のそれを取り出して、型自体に対して直接それを評価する)以下の式が実際に真と評価されていることを確認します:

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

句はフィルタう唯一の理由式が真を評価していないためです。上のコード行を実行すると、実行しようとしている式が、あなたが思っているかどうかを評価するかどうかが明確に示されます。そうでない場合は、適切な動作が得られるまで調整し、新しい式をLINQステートメントに挿入します。

+0

事は、両方の式が同一であることです。唯一の違いはリストからのものと、遅延評価された列挙子です。 –

+0

私は、怠惰な列挙子とリストが同じように動作すると予想します。あなたが部分的に評価するのか、複合的な評価をするのかは関係ありません。結果は同じでなければなりません。少なくとも私はIEnumerable とLINQを理解しています。 – jrista

2

これは正しい結果をもたらすようにも見えますが、私はなぜそれがわかりません。

foreach、このクエリと元のクエリの唯一の違いは、元のクエリが遅延評価されていることです。なぜ型が静的なのかが分かりません。確かに分かりません。

+1

違いは、このように、すべての依存するアセンブリを最初に読み込み、 'IsAssignableFrom'をチェックすることです。チェックの時点でLINQクエリが1つしかない場合、読み込まれた依存アセンブリは、既に反復処理されたアセンブリのものです。ここで「従属」とは、反復処理を行う型の基本型/実装インタフェースを含むアセンブリを意味します。それでも、なぜこれが何か変わるべきなのかまだ分かりません。 –

関連する問題