4

私は持っていObservableCollection aますList b

aここで、 list に相当する要素をコレクションから削除したいと思いますb

現時点での私のコード:

public static void CrossRemove<TFirst, TSecond>(this ObservableCollection<TFirst> collection, IEnumerable<TSecond> secondCollection, Func<TFirst, TSecond, bool> predicate)
{
    collection.Where(first => secondCollection.Any(second => predicate(first, second)))
        .ToList().ForEach(item => collection.Remove(item));
}

利用方法:

ObservableCollection<string> first = new ObservableCollection<string> { "1", "2", "3", "4", "5", "6", "k" };

IEnumerable<int> second = new List<int> { 2, 3, 5 };

first.CrossRemove(second, (x, y) => x == y.ToString());

このコードは、コレクションから「2」、「3」、および「5」を削除し、「1」、「4」、「6」、および「k」を残します。

私の実際のコードabは、同じものを継承する要素が含まれinterfaceており、そのインターフェイスにあるプロパティを比較していますが、それを利用することはできません。

wpfビューにバインドされているため、新しいリストを作成できません。アイテムを削除する代わりに作成すると、目に見える不具合が発生します。

これを行うためのより良い/より速い方法はありますか?

4

2 に答える 2

1

2 番目のコレクションを作成して、HashSet<T>ルックアップを高速化できます。私もあなたForEachを に変更しましたforeach。これは、元のように、プロパティを使用して簡単に実証できます。

void Main()
{
    ObservableCollection<MyClass> first = new ObservableCollection<MyClass> { "1", "2", "3", "4", "5", "6", "k" };

    ISet<IMyInterface> second = new HashSet<IMyInterface>(new MyClass2[] { 2, 3, 5 }, new MyEqualityComparer());

    first.CrossRemove(second);

    Console.WriteLine(string.Join(", ", first.Select(x => x.MyProperty)));
    // 1, 4, 6, k
}
public interface IMyInterface
{
    string MyProperty { get; set; }
}
public class MyEqualityComparer : IEqualityComparer<IMyInterface>
{
    public bool Equals(IMyInterface a, IMyInterface b)
    {
        return a.MyProperty == b.MyProperty;
    }
    public int GetHashCode(IMyInterface obj)
    {
        return obj.MyProperty.GetHashCode();
    }
}
public static class Extensions
{
    public static void CrossRemove<TFirst, TSecond>(this ObservableCollection<TFirst> collection, ISet<TSecond> set) where TFirst : TSecond
    {
        foreach (var item in collection.Where(item => set.Contains(item)).ToList())
            collection.Remove(item);
    }
}
public class MyClass : IMyInterface
{
    public string MyProperty { get; set; }
    public static implicit operator MyClass(string s)
    {
        return new MyClass { MyProperty = s };
    }
}
public class MyClass2 : IMyInterface
{
    public string MyProperty { get; set; }
    public static implicit operator MyClass2(int i)
    {
        return new MyClass2 { MyProperty = i.ToString() };
    }
}

IEqualityComparer<object>オブジェクトが共通のインターフェースを共有していない場合でも、ラムダ述語が次のようになっている場合など、両方で正しく機能するを記述できるはずです。

(TypeA a, TypeB b) => a.PropA == b.PropB

次に、クラスは次のようになります。

public class MyOtherEqualityComparer : IEqualityComparer<object>
{
    private object GetProperty(object obj)
    {
        if (obj is TypeA)
            return ((TypeA)obj).PropA;
        else if (obj is TypeB)
            return ((TypeB)obj).PropB;
        else
            throw new Exception();
    }
    public bool Equals(object a, object b)
    {
        return GetProperty(a).Equals(GetProperty(b));
    }
    public int GetHashCode(object obj)
    {
        return GetProperty(obj).GetHashCode();
    }
}
于 2013-10-28T15:27:39.663 に答える
0

List<T>これを行う最も簡単な方法は、より一般的であるため、のRemoveAll関数と同等のものを使用することだと思います。それ以外の

first.CrossRemove(second, (x, y) => x == y.ToString());

私は書くだろう

first.RemoveAll(item1 => second.Any(item2 => item1 == item2.ToString()));

残念ながら、 にObservableCollection<T>はこのメソッドがないため、次のように記述する必要があります。

public static class Extensions
{
    public static void RemoveAll<T>(this ICollection<T> collection, Func<T, bool> pred)
    {
        var toBeRemoved = collection.Where(pred).ToArray();
        foreach (var item in toBeRemoved)
            collection.Remove(item);
    }
}

編集:

上記の拡張方法は非常に非効率的です。このような他の方法は、アルゴリズム的にはるかに高速です。ObservableCollection<T>ただし、この場合は、おそらくビューにバインドされている について話しているため、関連性があるとは思いません。これを考えると、ごく少数の変更のみを行う必要があります。そうしないと、レイアウトと再レンダリングのコストが非常に高くなります。多数の変更を行っている場合は、レイアウトが一度だけ再計算されるように、コレクションを新しいものに置き換える必要があります。

于 2014-05-08T01:02:09.493 に答える