2009-06-26 2 views
1

일반 콜렉션에서 하나의 오브젝트 (First, Min, Max 등)를 리턴하는 Linq 메소드 사용에 대해이 question에 대해 질문했습니다. 이제 linq의 Except() 메소드를 사용할 수 있기를 원하며 어떻게해야할지 모르겠습니다. 아마 대답은 내 앞에 있지만 도움이 필요하다고 생각합니다.
해당 설명 필드에 대해 누락 날짜를 채우는 일반적인 방법이 있습니다. 이 방법은 다음과 같이 선언됩니다.일반 콜렉션에서 Except() 사용

public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection, string datePropertyName, string descriptionPropertyName) 
    { 
     Type type = typeof(T); 
     PropertyInfo dateProperty = type.GetProperty(datePropertyName); 
     PropertyInfo descriptionProperty = type.GetProperty(descriptionPropertyName); 
     ... 
    } 

내가 원하는 것은 이것입니다. datePropertyName은 날짜 간격을 채우는 데 사용할 날짜 속성의 이름입니다 (컬렉션에 아직없는 날짜의 기본 객체 인스턴스 추가). '비 제네릭 클래스를 처리 한 경우에, 나는 이런 식으로 뭔가 할 것 :

foreach (string description in descriptions) 
{ 
    var missingDates = allDates.Except(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList()); 
... 
} 

을하지만 런타임에서 해결 dateProperty 및 descriptionProperty 특성을 가진 일반적인 방법 FillInMissingDates를 사용하여 동일한을 어떻게 할 수 있습니까?

+1

당신은 우리에게 당신의 제네릭 클래스의 정의를 표시해야합니다 (: 이처럼 사용할 필요가 또는 적어도 관련 비트). – Noldorin

+0

Noldorin, 형식은 FillInMissingDates 제네릭 메서드 선언에서와 마찬가지로 T입니다. –

+0

@ 구스타보 : 무슨 뜻인지 잘 모르겠습니다. 제네릭 형식 T를 제한하지 않으면 datePropertyName 속성의 존재를 보장 할 수 없습니다. – Noldorin

답변

3

여러분의 메서드에서 사용하려는 모든 속성을 가진 인터페이스를 정의하는 것이 가장 좋은 방법이라고 생각합니다. 이 인터페이스를 구현할 때 메소드가 사용할 수있는 클래스를 갖도록하십시오. 그런 다음 일반 메소드를 사용하고 인터페이스에서 파생되도록 제네릭 유형을 제한하십시오.

이 예제는 원하는대로 정확하게 수행되지 않을 수 있습니다. 설명에 일치하는 항목의 누락 날짜를 채우지 만 잘하면 기본 아이디어를 제공합니다.

public interface ITransactable 
{ 
    string Description { get; } 
    DateTime? TransactionDate { get; } 
} 

public class CompletedTransaction : ITransactable 
{ 
    ... 
} 

// note conversion to extension method 
public static void FillInMissingDates<T>(this IEnumerable<T> collection, 
              string match, 
              DateTime defaultDate) 
     where T : ITransactable 
{ 
     foreach (var trans in collection.Where(t => t.Description = match)) 
     { 
      if (!trans.TransactionDate.HasValue) 
      { 
       trans.TransactionDate = defaultDate; 
      } 
     } 
} 

호출하기 전에 (최소한 C# 4.0이 나올 때까지) 열거 형을 ITransactable로 전송해야합니다.

var list = new List<CompletedTransaction>(); 

list.Cast<ITransactable>() 
    .FillInMissingDates("description",DateTime.MinValue); 

또는, VS2008 Samples 컬렉션 Dynamic LINQ를 사용하여 조사 할 수있다. 이렇게하면 클래스간에 일관성이없는 경우 속성의 이름을 지정할 수 있습니다. 그러나 리플렉션을 사용하여 속성을 설정해야 할 수도 있습니다.

public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection, 
    Func<T, DateTime> dateProperty, Func<T, string> descriptionProperty, string desc) 
{ 
    return collection.Except(collection 
     .Where(d => descriptionProperty(d) == desc)) 
     .Select(d => dateProperty(d)); 
} 

이것은 당신이 같은 일을 수행 할 수 있습니다 :

+0

안녕하세요, tvanfosson. 귀하의 답변에 감사드립니다. 클래스 정의에 대한 인터페이스가 없기 때문에 인터페이스를 사용하고 싶지 않습니다. 내가 편집 한 후 질문에서 설명했듯이, 리플렉션을 사용하여 이름을 기반으로 실제 속성을 얻고 있습니다. 감사! –

0
foreach (string description in descriptions) 
{  
var missingDates = allDates.Except<YourClass>(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList()); 
} 

사실 C#의 거의 모든 LINQ 확장은 일반적인 가능한 값을 갖습니다. (예외 및 예외)

+0

그건 내가 생각하기에 그가 말하는 것이 아니야. 그는 * YourClass *가 Except 확장 방법이 아닌 일반적인 것임을 의미합니다. 그는 심지어 자신의 예제 형식 유추에 보여 그의 경우에는 필요하지 않습니다. – Noldorin

+0

당신은 Noldorin 맞습니다. 나는 속성 설명 (따라서, descriptionPropertyName 매개 변수)의 이름이 무엇인지 알지 못합니다. 질문에 더 많은 정보를 추가했습니다. 감사. –

0

문자열 이름으로 액세스 할 속성을 식별하려는 경우 제네릭을 사용할 필요가 없습니다. 그들의 유일한 목적은 정적 유형 안전입니다. 리플렉션을 사용하여 속성에 액세스 한 다음 비제로 IEnumerable에서 작동하는 방식으로 만듭니다.

+0

이것은 분명히 그가 찾고있는 것일뿐입니다. – Noldorin

1

당신은이 방법을 시도 할 수는 반드시 Except() 전화를하지 않아도

someCollection.FillInMissingDates(o => o.CreatedDate, o => o.Description, "matching"); 

참고, 그냥이 :

.. Where(d => descriptionProperty(d) != desc) 
0

받기 맞춤 데이터 클래스로 작동하는 여러 속성을 제외하고 결과가 허용되지 않습니다. 에드.(msdn 101 LINQ Samples에 주어진)

public void Linq53() 
{ 
    List<Product> products = GetProductList(); 
    List<Customer> customers = GetCustomerList(); 

    var productFirstChars = 
     from p in products 
     select p.ProductName[0]; 
    var customerFirstChars = 
     from c in customers 
     select c.CompanyName[0]; 

    var productOnlyFirstChars = productFirstChars.Except(customerFirstChars); 

    Console.WriteLine("First letters from Product names, but not from Customer names:"); 
    foreach (var ch in productOnlyFirstChars) 
    { 
     Console.WriteLine(ch); 
    } 
} 

키를 갖는, 그에 따라 데이터를 처리 할 수 ​​있습니다 :)

관련 문제