IENUMERABLEで変更を検出します
-
29-10-2019 - |
質問
この形式ではまだ尋ねられていないことに驚いているという質問があります。
データのソースを介した反復に基づいて生成されたienumerableがある場合(および利回りの返品ステートメントを使用する)、列を介して生成された列挙器を介してアクセスした後にソースの変更があったことを検出するにはどうすればよいですかGetEnumerator Call?
ここに奇妙な部分があります:私はマルチスレッドではありません。私の質問にはどこかに欠陥があると思います。これは簡単なはずだからです。 。 。ソースがいつ変更され、イテレーターが古くなっているかを知りたいだけです。
どうもありがとう。
解決
この情報を追跡するために、または最小限の使用で、列挙者の作成を自分で処理する必要があります yield return;
独自のタイプの変更追跡が配置されています。
たとえば、フレームワークコレクションクラスのほとんどは、「バージョン」番号を保持します。彼らが列挙者を作るとき、彼らはそのバージョン番号のスナップショットを保持し、それをチェックしてください MoveNext()
. 。呼び出す前に同じチェックをすることができます yield return XXX;
他のヒント
.NET BCLのほとんどのコレクションクラスは、変更追跡にバージョン属性を使用しています。つまり、列挙器はバージョン番号(整数)で構築され、バージョン番号の元のソースが各反復(Movenextが呼び出されたとき)と同じであることが確認されます。コレクションは、変更が行われるたびにバージョン属性を増加させます。この追跡メカニズムはシンプルで効果的です。
私が見た2つの他の方法は次のとおりです。
コレクションには、優れた列挙器への弱い参照を含む内部コレクションを保持します。また、コレクションに変更が加えられるたびに、まだ無効な各列挙器が無効になります。
または、コレクションにイベントを実装し(inotifyCollectionChanged)、列挙器でそのイベントに登録するだけです。上げた場合、列挙器を無効としてマークします。この方法は比較的簡単に実装でき、ジェネリックであり、頭上にはあまりありませんが、イベントをサポートするためにコレクションが必要です
Microsoftは、Ienumerableコレクションの変更は既存のIENumeratorオブジェクトを無効にする必要があることを提案しますが、そのポリシーが特に役立つことはめったになく、時には迷惑になる可能性があります。 IENUMERABLE/IENUMERATORの著者が、IENUMARATORがそのような変更なしで戻ってきたのと同じデータを返すことを妨げない方法でコレクションが変更された場合、例外をスローする必要性を感じる必要がある理由はありません。私はさらに進んで、可能であれば、列挙者が次の制約に従うことができる場合は機能的なままであることが望ましいと見なされるべきであることを提案します。
- 列挙期間中、コレクション内にあるアイテムは、正確に1回返品する必要があります。
- 列挙中に追加または削除された各アイテムは、ゼロまたは1回返品されますが、1回以下です。オブジェクトがコレクションから削除されて再添加された場合、それは最初に1つのアイテムに収容されていたが新しいアイテムに入れられたと見なされる可能性があるため、列挙は古いもの、新しいもの、両方、またはどちらも合法的に返還する可能性があります。
VisualBasic.Collectionクラスは、上記の制約に従って動作します。このような動作は非常に有用であり、クラスを列挙し、特定の基準を満たすアイテムを削除することが可能になります。
もちろん、列挙中に変更された場合に賢明に動作するコレクションを設計することは、必ずしも例外を投げるよりも簡単ではないかもしれませんが、列挙者がコレクションをリストに変換し、リスト。必要に応じて、特にスレッドの安全が不要な場合は、コレクションに列挙者によって返されたリストへの強いまたは弱い参照を保持し、変更されるたびにそのような参照を無効にすることが役立つ場合があります。別のオプションは、コレクションへの「実際の」参照をラッパークラスに保持し、内部クラスに列挙者の数をカウントしてもらうことです(列挙者は実際のコレクションへの参照を取得します)。列挙者が存在している間にコレクションを変更する試みが行われた場合は、コレクションインスタンスをコピーに置き換えてから、コピーを変更します(コピーはゼロの参照カウントで開始されます)。このようなデザインは、IENumeratorが処分されずに放棄されるシナリオを除き、リストの冗長なコピーを作成することを避けます。そのシナリオでさえ、弱者やイベントを含むシナリオとは異なり、必要以上に生かされないオブジェクトはありません。
私は答えを見つけていませんが、周りの仕事として、私はこのような例外をキャッチしています(WPF例):
while (slideShowOn)
{
if (this.Model.Images.Count < 1)
{
break;
}
var _bitmapsEnumerator = this.Model.Images.GetEnumerator();
try
{
while (_bitmapsEnumerator.MoveNext())
{
this.Model.SelectedImage = _bitmapsEnumerator.Current;
Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle, null);
Thread.Sleep(41);
}
}
catch (System.InvalidOperationException ex)
{
// Scratch this bit: the error message isn't restricted to English
// if (ex.Message == "Collection was modified; enumeration operation may not execute.")
// {
//
// }
// else throw ex;
}
}