Jeśli używasz to:
var newData = destination.Except(data.Select(x => f(x)));
Musisz projektu „dane” do samego rodzaju zawartej w " destination”, ale przy użyciu kodu poniżej ciebie mógł pozbyć się tego ograniczenia:
//Here is how you can compare two different sets.
class A { public string Bar { get; set; } }
class B { public string Foo { get; set; } }
IEnumerable<A> setOfA = new A[] { /*...*/ };
IEnumerable<B> 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<TFirst> Except<TFirst, TSecond, TCompared>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TCompared> firstSelect,
Func<TSecond, TCompared> secondSelect)
{
return Except(first, second, firstSelect, secondSelect, EqualityComparer<TCompared>.Default);
}
public static IEnumerable<TFirst> Except<TFirst, TSecond, TCompared>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TCompared> firstSelect,
Func<TSecond, TCompared> secondSelect,
IEqualityComparer<TCompared> comparer)
{
if (first == null)
throw new ArgumentNullException("first");
if (second == null)
throw new ArgumentNullException("second");
return ExceptIterator<TFirst, TSecond, TCompared>(first, second, firstSelect, secondSelect, comparer);
}
private static IEnumerable<TFirst> ExceptIterator<TFirst, TSecond, TCompared>(
IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst, TCompared> firstSelect,
Func<TSecond, TCompared> secondSelect,
IEqualityComparer<TCompared> comparer)
{
HashSet<TCompared> set = new HashSet<TCompared>(second.Select(secondSelect), comparer);
foreach (TFirst tSource1 in first)
if (set.Add(firstSelect(tSource1)))
yield return tSource1;
}
}
Niektórzy mogą twierdzić, że to pamięć nieefektywne ze względu na zastosowanie HashSet. Ale faktycznie metoda Enumerable.Except ramy robi to samo z podobną klasą wewnętrzną o nazwie "Set" (przyjrzałem się przez dekompilację).
Zła, stara pętla 'for', kiedyś była tak użyteczna, ale, niestety, nigdy nie uszczęśliwia ludzi z jednym liniowcem. – Marc
@Marc: Nie zgadzam się z sentymentem, który wyrażasz. Mamy teraz sposoby pisania kodu, które wyraźniej wyrażają intencje, nie martwiąc się o mechanizm. 'for' wyraża mechanizmy i ukrywa intencje. Oparte na LINQ jedno-liniowce, które często dekodujesz (tak, nie zawsze) lepiej wyrażają intencje i ukrywają mechanizmy. Prowadzi to do kodu, który jest łatwiejszy do zrozumienia i utrzymania. – jason
@Jason, podczas gdy byłem niepoważny, wszelkie funkcje, które rzucasz na projekcję taką jak ty, zapewniają jedynie założenie intencji. – Marc