1

ObservableCollection<T>変化するコレクションを表すクラスがあります。

public interface IObservableCollection<T> : IObservable<IEnumerable<T>>
{
    void Add(T item);
    void Remove(T item);
}

アイテムが追加または削除されると、内部的に aのメソッドSubject<IEnumerable<T>>が、 のメソッドを通じて公開されるOnNextnew で呼び出されます。IEnumerable<T>SubscribeIObservableCollection<T>

私もクラスを持っていますPerson

public interface IPerson
{
    string Name { get; }
    IObservable<int> Position { get; }
}

私がやりたいことは、IEnumerable<Tuple<string, int>>各人の位置を表すストリームを作成することです。つまり、コレクション内のどこに人がいるかです。これは比較的簡単に思えます:

var peopleCollectionStream = new ObservableCollection<IPerson>();

var peoplePositions = from people in peopleCollectionStream
                      from updateList in
                          (from person in people
                           select person.Position.Select(pos => Tuple.Create(person.Name, pos)))
                           .CombineLatest()
                      select updateList;

次のようにストリームをサブスクライブできるようになりました。

peoplePositions
    .Subscribe(people =>
    {
        Console.WriteLine("Something was updated");
        foreach (var personTuple in people)
            Console.WriteLine("{0} -> {1}", personTuple.Item1, personTuple.Item2);
    });

そして、私は目的の出力を取得します:

var alice = new Person() { Name = "Alice" };
peopleCollectionStream.Add(alice);        // Alice -> 0
alice.Move(2);                            // Alice -> 2
var bob = new Person() { Name = "Bob" };
peopleCollectionStream.Add(bob);          // Alice -> 2, Bob -> 0
bob.Move(3);                              // Alice -> 2, Bob -> 3

コレクションから人物を削除したい場合に問題が発生し、その更新をストリームから除外します。

peopleCollectionStream.Remove(bob);       // Alice -> 2
bob.Move(4);                              // Alice -> 2, Bob -> 4

ボブがコレクションから削除された場合、ボブの位置の更新が含まれないようにしたいと考えています。これどうやってするの?

4

2 に答える 2

1

この種の機能的なことをしたい場合、追加イベントと削除イベントを操作しようとするのは悪い考えであることがわかりました。削除と追加を一致させ、基礎となるコードもそれを行うようにすることは、大変な作業です。

代わりに、腐りやすいアイテム/コレクションを使用しています。各アイテムをライフタイム (キャンセル トークン) とペアにします。アイテムは、ライフタイムが終了すると削除されたと見なされます。次に、他のものを配線するときにそれらのライフタイムを使用します。と呼ばれるコレクション タイプを使用しますPerishableCollection<T>。これは、ライフタイムとペアになったアイテムを取得し、その内容を として見ることができますIObservable<Perishable<T>>

腐りやすいコレクションに関するブログ投稿を書き、参照できる nuget ライブラリを公開しました。

腐りやすいコレクションの腐りやすいコレクションをフラット化するコードは次のとおりです。

public static PerishableCollection<T> Flattened<T>(this PerishableCollection<PerishableCollection<T>> collectionOfCollections, Lifetime lifetimeOfResult) {
    if (collectionOfCollections == null) throw new ArgumentNullException("collectionOfCollections");

    var flattenedCollection = new PerishableCollection<T>();
    collectionOfCollections.CurrentAndFutureItems().Subscribe(
        c => c.Value.CurrentAndFutureItems().Subscribe(

            // OnItem: include in result, but prevent lifetimes from exceeding source's lifetime
            e => flattenedCollection.Add(
                item: e.Value,
                lifetime: e.Lifetime.Min(c.Lifetime)),

            // subscription to c ends when the c's lifetime ends or result is no longer needed
            c.Lifetime.Min(lifetimeOfResult)),

        // subscription ends when result is no longer needed
        lifetimeOfResult);

    return flattenedCollection;
}

上記は、コレクションのコレクションに追加されたコレクションを受け取るようにサブスクライブし、アイテムを受け取るためにサブスクライブする各コレクションに対して機能します。アイテムは結果のコレクションに配置され、アイテムが死ぬか、そのコレクションが死ぬとライフタイムが終了します。メソッドに与えられた有効期間が終了すると、すべてのサブスクリプションが終了します。

この問題に対処する別の方法は、 を平坦化するメソッドを作成することIObservable<Perishable<IObservable<Perishable<T>>>>です。これには、呼び出し元が結果の有効期間を明示的に管理する必要がなく、より多くの状況で適用できるという利点があります。ただし、スレッドセーフな方法でシーケンスの失敗/完了を処理する必要があるため、そのメソッドを記述するのははるかに困難です。

flatten メソッドの使用例を次に示します (新しいコンソール アプリケーションを作成し、腐敗しやすいコレクションを参照し、上記のメソッドとこれも貼り付けます)。

using TwistedOak.Collections;
using TwistedOak.Util;

static void Main() {
    var p = new PerishableCollection<PerishableCollection<string>>();
    var f = p.Flattened(Lifetime.Immortal);
    f.CurrentAndFutureItems().Subscribe(e => {
        Console.WriteLine("{0} added to flattened", e.Value);
        e.Lifetime.WhenDead(() => Console.WriteLine("{0} removed from flattened", e.Value));
    });

    // add some 'c' items to f via p
    var c = new PerishableCollection<string>();
    var cLife = new LifetimeSource();
    c.Add("candy", Lifetime.Immortal);
    p.Add(c, cLife.Lifetime);
    c.Add("cane", Lifetime.Immortal);

    // add some 'd' items to f via p
    var d = new PerishableCollection<string>();
    p.Add(d, Lifetime.Immortal);
    d.Add("door", Lifetime.Immortal);
    d.Add("dock", Lifetime.Immortal);


    // should remove c's items from f via removing c from p
    cLife.EndLifetime();
}

コードは次を出力する必要があります。

candy added to flattened
cane added to flattened
door added to flattened
dock added to flattened
candy removed from flattened
cane removed from flattened

これで、より簡単な道を歩み始めることができれば幸いです。

于 2013-07-11T11:17:44.140 に答える
0

答えは.Switchオペレーターです。サブスクライブするオブザーバブルの最新のリストのみを選択することにより、ストリームはコレクションの最新バージョンに存在しないものを除外します。

var peoplePositions = (from people in peopleCollectionStream
                       select
                           (from person in people
                            select person.Position
                                .Select(pos => Tuple.Create(person.Name, pos))
                            ).CombineLatest()
                       ).Switch();

(ちなみに、ブラケット/ネストされた linq クエリ構文を使用する場合のフォーマットについて誰か良い推奨事項がある場合は、私に知らせてください。上記は非常にひどいようです!)

于 2013-07-11T13:11:24.993 に答える