在Generic集合上使用Except()

时间:2009-06-26 18:09:55

标签: c# linq generics

我已经问过这个question关于使用一个Linq方法,该方法从泛型集合中返回一个对象(First,Min,Max等)。 我现在想要能够使用linq的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());
...
}

但是如何使用泛型方法FillInMissingDates以及在运行时解析的dateProperty和descriptionProperty属性来做同样的事情?

5 个答案:

答案 0 :(得分: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进行调查。如果属性在类之间不一致,则允许您指定属性的名称。但是,您可能仍需要使用反射来设置属性。

答案 1 :(得分: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)

答案 2 :(得分:0)

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

事实上,C#中的几乎所有LINQ扩展都具有通用的可能值。 (除了和除外)

答案 3 :(得分:0)

如果要识别要通过字符串名称访问的属性,则不需要使用泛型。它们的唯一目的是静态类型安全。只需使用反射来访问属性,并使该方法适用于非通用IEnumerable

答案 4 :(得分: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); 
    } 
}

拥有密钥后,您可以相应地处理您的数据:)