2009-09-04 3 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 : 내가 발발 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를 구현하는 하나의 유형이 있습니다. 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(); 
     } 
    } 
} 

두 개의 코드에서 볼 수있는 어셈블리에서 큰 차이가 있음을 눈치 채고 있습니다. 즉, 두 번째 함수 인 Bar은 linq 기반의 어셈블리가 볼 수없는 어셈블리를 볼 수 있습니다.

더 흥미로운 것은 내가 실행 순서를 바꾸면, 이제는 Foo이 다른 하나가 할 수없는 어셈블리를 볼 수 있다는 것입니다. 즉 정확한 리버스입니다. 내가 두 번 첫 번째 쿼리를 실행하면

마지막으로, 출력은 그래서 동일합니다 :

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

모든

은 동일한 결과를 생성합니다.

내 유일한 가정은 다른 쿼리가로드되지 않는 하나의 쿼리에 의해로드되는 일부 어셈블리입니다.

+0

아주 좋은 물건들 -이 Keeper의 모든 작업에 감사드립니다. – Daniel

0

저는 정말로 LINQ 전문가는 아니지만, 추측 할 위험이 있습니다. 이것이 내가 Hibernate에서 encounted 문제 (자바 ORM 도구)과 유사하게 나타납니다 최대 절전 모드에서

, 컬렉션 속성은 느리게 초기화하도록 설정할 수 있습니다. 프로퍼티가 초기화되지 않을 때, Hibernate는 Parent를 서브 클래스 화하고 자체적 인 동작을 추가하여 지연로드 작업을 수행함으로써 바이트 코드 도구를 사용하여 프록시를 생성합니다. 내가 좋아하는 클래스가있는 경우 :

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

class Parent extends Child { 
} 

class Child { 
} 

내가 getEntities()를 호출 부모 개체의 컬렉션을 반환합니다. 내 getEntities가 게으르다 고 표시되기 때문에, 내가 가져온 객체는 자동으로 Parent의 하위 클래스로 생성됩니다. 컬렉션의 항목 중 하나가 하위를 나타내더라도 실제 객체는 실제 자식 객체가 아니라 자식 객체의 프록시이기 때문에 "myEntity instanceof Child"와 같은 검사가 작동하지 않습니다.

LINQ는 데이터 액세스 또는 개체에 사용할 수있는 쿼리 메커니즘이라는 것을 알고 있습니다. 위의 경우와 마찬가지로 where 절에있는 "type"객체가 실제 Type 객체에 대한 일종의 쿼리입니다. 따라서 isAssignable()은 프록시 객체가 IResourceConverter를 구현하지 않는다고 결정합니다.

0

실제로 아래의 표현식이 true로 평가 있는지 확인합니다 (즉,이 LINQ 문 밖으로 가지고 가고, 유형 자체에 대해 직접 평가) :

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

조항이를 필터링 할 경우 유일한 이유 표현식이 사실을 평가하지 않기 때문에 결과가 나옵니다. 위의 코드 행을 실행하면 실행하려는 표현이 생각하는대로 평가하는지 아닌지를 분명하게 나타내야합니다. 그렇지 않은 경우 적절한 동작을 얻을 때까지 조정 한 다음 LINQ 문에 새 식을 넣습니다.

+0

두 표현식은 동일합니다. 단 하나의 차이점은 목록에서 나오고 다른 하나는 느리게 평가 된 열거 자입니다. –

+0

나는 게으른 열거 자와 목록이 동일하게 동작 할 것으로 기대합니다. 당신이 부분적으로 또는 합성 된 전체로서 평가한다면 그것은 중요하지 않습니다 ... 결과는 동일해야합니다. 적어도 IEnumerable 과 LINQ를 이해해야합니다. – jrista

2

올바른 결과가 나오는 것처럼 보이지만 이유가 확실하지 않습니다.

foreach,이 쿼리 및 원래 쿼리 간의 유일한 차이점은 원래 쿼리가 느리게 평가된다는 것입니다. 유형이 고정되어 있기 때문에 이것이 왜 효과가 있는지 나는 확신 할 수 없다.

+1

다른 점은 이런 식으로 모든 종속 어셈블리를 먼저로드 한 다음 IsAssignableFrom을 확인해야한다는 것입니다. 검사하는 동안 LINQ 쿼리가 하나만있을 때 이미로드 된 종속 어셈블리는 이미 반복 된 어셈블리의 유일한 어셈블리입니다. 여기에서 "의존적"이란 말은 반복되는 타입의 기본 유형/구현 된 인터페이스를 포함하는 어셈블리를 의미합니다. 그럼에도 불구하고, 나는 이것이 왜 어떤 차이를 만들어야하는지 아직 모르겠다. –

관련 문제