LINQ:通过使它们可转换/可比较,对不同类型的集合使用.Except()吗?

给定两个不同类型的列表,是否可以使这些类型在彼此之间可转换或相互比较(例如使用TypeConverter或类似),以便LINQ查询可以比较它们? 我已经在SO上看过其他类似的问题,但没有任何指出使类型可以在彼此之间进行转换以解决问题。

收集类型:

public class Data { public int ID { get; set; } } public class ViewModel { private Data _data; public ViewModel(Data data) { _data = data; } } 

所需用法:

  public void DoMerge(ObservableCollection destination, IEnumerable data) { // 1. Find items in data that don't already exist in destination var newData = destination.Except(data); // ... } 

这似乎是合乎逻辑的,因为我知道如何将ViewModel的实例与Data的实例进行比较,我应该能够提供一些比较逻辑,然后LINQ将用于查询,如.Except()。 这可能吗?

您最好的选择是提供从DataViewModel的投影,以便您可以说

 var newData = destination.Except(data.Select(x => f(x))); 

其中fData映射到ViewModel 。 您还需要一个IEqualityComparer

我假设提供从DataViewModel的投影是有问题的,所以除了Jason之外我还提供另一个解决方案。

除了使用哈希集(如果我没记错),您可以通过创建自己的哈希集来获得类似的性能。 我还假设您在IDs相等时将Data对象识别为相等。

 var oldIDs = new HashSet(data.Select(d => d.ID)); var newData = destination.Where(vm => !oldIDs.Contains(vm.Data.ID)); 

您可能在方法的其他位置使用另一个“oldData”集合,在这种情况下,您可能希望这样做。 在数据类上实现IEquatable ,或者为哈希集创建自定义IEqualityComparer

 var oldData = new HashSet(data); //or: var oldData = new HashSet(data, new DataEqualityComparer()); var newData = destination.Where(vm => !oldData.Contains(vm.Data)); 

如果你使用这个:

 var newData = destination.Except(data.Select(x => f(x))); 

您必须将“数据”投影到“目标”中包含的相同类型,但使用下面的代码可以摆脱此限制:

 //Here is how you can compare two different sets. class A { public string Bar { get; set; } } class B { public string Foo { get; set; } } IEnumerable setOfA = new A[] { /*...*/ }; IEnumerable setOfB = new B[] { /*...*/ }; var subSetOfA1 = setOfA.Except(setOfB, a => a.Bar, b => b.Foo); //alternatively you can do it with a custom EqualityComparer, if your not case sensitive for instance. var subSetOfA2 = setOfA.Except(setOfB, a => a.Bar, b => b.Foo, StringComparer.OrdinalIgnoreCase); //Here is the extension class definition allowing you to use the code above public static class IEnumerableExtension { public static IEnumerable Except( this IEnumerable first, IEnumerable second, Func firstSelect, Func secondSelect) { return Except(first, second, firstSelect, secondSelect, EqualityComparer.Default); } public static IEnumerable Except( this IEnumerable first, IEnumerable second, Func firstSelect, Func secondSelect, IEqualityComparer comparer) { if (first == null) throw new ArgumentNullException("first"); if (second == null) throw new ArgumentNullException("second"); return ExceptIterator(first, second, firstSelect, secondSelect, comparer); } private static IEnumerable ExceptIterator( IEnumerable first, IEnumerable second, Func firstSelect, Func secondSelect, IEqualityComparer comparer) { HashSet set = new HashSet(second.Select(secondSelect), comparer); foreach (TFirst tSource1 in first) if (set.Add(firstSelect(tSource1))) yield return tSource1; } } 

有些人可能认为由于使用了HashSet,内存效率低下。 但实际上框架的Enumerable.Except方法对一个名为’Set’的类似内部类做了同样的事情(我看了一下反编译)。