C# 在泛型集合上使用 except()

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1050456/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 07:01:53  来源:igfitidea点击:

Using Except() on a Generic collection

c#linqgenerics

提问by Gus Cavalcanti

I have asked this questionabout using the a Linq method that returns one object (First, Min, Max, etc) from of a generic collection. I now want to be able to use linq's Except() method and I am not sure how to do it. Perhaps the answer is just in front on me but think I need help.
I have a generic method that fills in missing dates for a corresponding descriptive field. This method is declared as below:

我问过这个关于使用从泛型集合返回一个对象(First、Min、Max 等)的 Linq 方法的问题。我现在希望能够使用 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);
       ...
    }

What I want to accomplish is this. datePropertyName is the name of the date property I will use to fill in my date gaps (adding default object instances for the dates not already present in the collection). If I were dealing with a non-generic class, I would do something like this:

我想要完成的就是这个。datePropertyName 是日期属性的名称,我将使用它来填充我的日期间隔(为集合中尚未存在的日期添加默认对象实例)。如果我正在处理一个非泛型类,我会做这样的事情:

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

But how can I do the same using the generic method FillInMissingDates with the dateProperty and descriptionProperty properties resolved in runtime?

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

采纳答案by tvanfosson

I think the best way would be to define an interface with all of the properties that you want to use in your method. Have the classes that the method may be used in implement this interface. Then, use a generic method and constrain the generic type to derive from the interface.

我认为最好的方法是定义一个接口,其中包含要在方法中使用的所有属性。具有该方法可用于实现该接口的类。然后,使用泛型方法并约束泛型类型从接口派生。

This example may not do exactly what you want -- it fills in missing dates for items in the list matching a description, but hopefully it will give you the basic idea.

这个例子可能不完全符合你的要求——它为列表中匹配描述的项目填充缺失的日期,但希望它能给你基本的想法。

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

You'll need to Cast your enumeration to ITransactable before invoking (at least until C# 4.0 comes out).

您需要在调用之前将您的枚举转换为 ITransactable(至少在 C# 4.0 出现之前)。

 var list = new List<CompletedTransaction>();

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

Alternatively, you could investigate using Dynamic LINQfrom the VS2008 Samplescollection. This would allow you to specify the name of a property if it's not consistent between classes. You'd probably still need to use reflection to set the property, however.

或者,你可以调查使用动态LINQVS2008样本采集。如果类之间不一致,这将允许您指定属性的名称。但是,您可能仍然需要使用反射来设置属性。

回答by Timotei

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

In fact, almost all LINQ extension in C# have a generic possible value. (Except and Except)

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

回答by Daniel Earwicker

If you're going to identify the property to be accessed by a string name, then you don't need to use generics. Their only purpose is static type safety. Just use reflection to access the property, and make the method work on a non-generic IEnumerable.

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

回答by Jason

You could try this approach:

你可以试试这个方法:

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

This allows you to do things like:

这允许您执行以下操作:

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

Note that you don't necessarily need the Except()call, and just have:

请注意,您不一定需要Except()调用,只需:

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

回答by Lakshay

Getting Except result with multiple properties working with custom data class is not allowed. You have to use it like this: (given in msdn 101 LINQ Samples)

不允许使用使用自定义数据类的多个属性获取例外结果。你必须像这样使用它:(在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); 
    } 
}

Having the key, you can handle your data accordingly :)

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