2009-06-26 2 views
1

Я попросил об этом question об использовании метода Linq, который возвращает один объект (первый, минимальный, и т. Д.) Из общей коллекции. Теперь я хочу использовать метод Except() linq, и я не уверен, как это сделать. Возможно, ответ прямо на меня, но думаю, что мне нужна помощь.
У меня есть общий метод, который заполняет отсутствующие даты для соответствующего описательного поля. Этот метод объявляется ниже:Использование исключения() в общей коллекции

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 - это имя свойства date, которое я буду использовать для заполнения пробелов в дате (добавление экземпляров объекта по умолчанию для дат, которые еще не присутствуют в коллекции). Если бы я иметь дело с необщим классом, я хотел бы сделать что-то вроде этого:

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

Но как я могу сделать то же самое, используя общий FillInMissingDates метода со свойствами dateProperty и descriptionProperty решенных во время выполнения?

+1

Вы должны показать нам определение вашего общего класса (или, по крайней мере, соответствующие биты), прежде чем мы сможем правильно ответить. – Noldorin

+0

Noldorin, тип T, как в объявлении общего метода FillInMissingDates. –

+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; 
      } 
     } 
} 

Вы должны Отдайте свою энумерацию ITransactable перед вызовом (по крайней мере до тех пор, C# 4.0 не выходит).

var list = new List<CompletedTransaction>(); 

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

В качестве альтернативы, можно исследовать с помощью Dynamic LINQ из VS2008 Samples коллекции. Это позволит вам указать имя свойства, если оно несовместимо между классами. Однако вам, вероятно, еще нужно использовать отражение, чтобы установить свойство.

+0

Привет, tvanfosson. Спасибо за Ваш ответ. Я не хотел бы использовать интерфейс, потому что у него не может быть интерфейса к определению класса. Как я объяснил в вопросе (после того, как я отредактировал), я использую размышления, чтобы получить фактические свойства, основанные на их именах. Благодаря! –

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

Фактически, почти все расширения LINQ в C# имеют общее возможное значение. (За исключением и за исключением)

+0

Это не то, о чем он говорит, я думаю. Он означает, что * YourClass * является общим, а не методом расширения. Он даже показывает, что в его типовом умозаключении делает это ненужным в его случае. – Noldorin

+0

Ты прав Нолдорин. Я не знаю, что такое имя свойства Description (таким образом, параметр descriptionPropertyName). Я добавил дополнительную информацию к вопросу. Благодарю. –

0

Если вы собираетесь идентифицировать свойство, к которому требуется доступ к имени строки, то вам не нужно использовать generics. Их единственной целью является безопасность статического типа. Просто используйте отражение, чтобы получить доступ к свойству, и сделайте метод работы с не общим IEnumerable.

+0

Это, вероятно, больше того, что он ищет, хотя я все еще жду объяснений. – Noldorin

1

Вы могли бы попробовать этот подход:

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

Это позволяет делать такие вещи, как:

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

Обратите внимание, что вам не обязательно нуждаются в Except() вызов, и просто:

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

Имея ключ, вы можете обрабатывать ваши данные соответственно :)

Смежные вопросы