Listオブジェクトがあります。リストを破棄するにはどうすればよいですか?
例えば、
List<User> usersCollection =new List<User>();
User user1 = new User();
User user2 = new User()
userCollection.Add(user1);
userCollection.Add(user2);
userCollection = null;
を設定するとどうなりますか?
foreach(User user in userCollection)
{
user = null;
}
どちらがベストですか?
最善のアイデアは、ガベージコレクターに任せることです。参照のみがリストの要素ではなくforeach
に設定されるため、null
は何もしません。リストをnull
に設定すると、実際にはガベージコレクションが発生する可能性があります(この投稿を参照してください C#:オブジェクト変数をnullに割り当てる必要がありますか? )。
第一に、リストはIDisposable
ではないためリストを「破棄」することはできません。また、リストを強制的に収集することはできませんC#が機能しないため。 通常実行しますnothingこちら。 mightを行う必要がある場合anything?
anythingが必要なのは、フィールド(またはcaptured variable/iterator block variable/etc)の場合だけですandインスタンス(/ delegate/iterator)は長生きします-その後、おそらくリストフィールドをnullに設定します。ただし、他のコードがまだリストへの参照を持っている場合、すべてが到達可能であることに注意してください。
リスト内のオブジェクトが不要になった場合、何もすべきではないことに同意しません。オブジェクトがインターフェースSystem.IDisposable
を実装している場合、オブジェクトの設計者は、オブジェクトに乏しいリソースがあると考えました。
オブジェクトが不要になり、オブジェクトにnullを割り当てるだけの場合、これらの乏しいリソースは、ガベージコレクターがオブジェクトを確定するまで解放されません。それまでは、このリソースを他の目的に使用することはできません。
例:ファイルからビットマップを作成し、ビットマップもファイルも不要にすることを検討します。コードは次のようになります。
using System.Drawing;
Bitmap bmp = new Bitmap(fileName);
... // do something with bmp until not needed anymore
bmp = null;
File.Delete(fileName); // EXCEPTION, filename is still accessed by bmp.
良い方法は次のとおりです。
bmp.Dispose();
bmp = null;
File.Delete(fileName);
リスト内のオブジェクト、またはコレクションの同じアカウント。 IDisposableであるコレクション内のすべてのオブジェクトは破棄する必要があります。コードは次のようになります。
private void EmptySequence (IEnumerable sequence)
{ // throws away all elements in the sequence, if needed disposes them
foreach (object o in sequence)
{
System.IDisposable disposableObject = o as System.IDisposable;
o = null;
if (disposableObject != null)
{
disposableObject.Dispose();
}
}
}
または、IEnumerable拡張関数を作成する場合
public static void DisposeSequence<T>(this IEnumerable<T> source)
{
foreach (IDisposable disposableObject in source.OfType(System.IDisposable))
{
disposableObject.Dispose();
};
}
これらのメソッドはすべてIEnumerableインターフェイスを実装しているため、すべてのリスト/辞書/読み取り専用リスト/コレクション/などでこれらのメソッドを使用できます。シーケンス内のすべてのアイテムがSystem.IDisposableを実装していない場合でも使用できます。
この投稿の別のアイデア...コレクションのすべてのメンバーを適切に破棄したい場合は、次の拡張メソッドを使用できます。
public static void DisposeAll(this IEnumerable set) {
foreach (Object obj in set) {
IDisposable disp = obj as IDisposable;
if (disp != null) { disp.Dispose(); }
}
}
これは、IDisposable
を実装し、それを破棄するメンバーのコレクションを調べます。実行中のコードから、次のようにリストをクリーンアップできます。
usersCollection.DisposeAll();
usersCollection.Clear();
これにより、すべてのメンバーにリソースを解放する機会が与えられ、結果のリストが空になります。
十分なコンテキストが提供されていません。ここではスコープが重要です。
GCは、nullに設定することなく、ユーザーとコレクションに割り当てられたメモリを処理するのに十分スマートであると思います。
コレクションがコレクションから必要のないユーザーを削除し、他のオブジェクトがそれらを参照しない場合、ヒントを提供することなく、GCされます。
オブジェクトへのライブ参照がある限り、GCはオブジェクトをクリーンアップしません。すべての参照を削除し、その仕事をすることができます。
IDisposableインターフェイスを実装するオブジェクトのリストを破棄するために使用できる拡張メソッドのさらに別の例。これはLINQ構文を使用します。
public static void Dispose(this IEnumerable collection)
{
foreach (var obj in collection.OfType<IDisposable>())
{
obj.Dispose();
}
}
最善の方法は
userCollection= null;
GCが休息の世話をします。
動作する一般的な実装(List<T>
メソッドリスト)アイテムが実装されている場合IDisposable
public static class LinqExtensions
{
public static void DisposeItems<T>(this IEnumerable<T> source) where T : IDisposable
{
foreach(var item in source)
{
item.Dispose();
}
}
}
このように使用される
if(list != null)
{
list.DisposeItems();
list.Clear();
}
System.Reactive.Disposeables を使用する場合、はるかに優れた方法があります。
タイプCompositeDisposable
の新しいプロパティを初期化し、このコレクションに使い捨てを追加するだけです。次に、これだけを廃棄します。
メモリリークを発生させずに、典型的なWPF/UWP ViewModelでそれを行う方法のコード例を次に示します。
_public sealed MyViewModel : IDisposable
{
// ie. using Serilog
private ILogger Log => Log.ForContext<MyViewModel>();
// ie. using ReactiveProperty
public ReactiveProperty<string> MyValue1 { get; }
= new ReactiveProperty<string>(string.Empty);
public ReactiveProperty<string> MyValue1 { get; }
= new ReactiveProperty<string>(string.Empty);
// this is basically an ICollection<IDisposable>
private CompositeDisposable Subscriptions { get; }
= new CompositeDisposable();
public MyViewModel()
{
var subscriptions = SubscribeToValues(); // Query
Subscriptions.AddRange(subscriptions); // Command
}
private IEnumerable<IDisposable> SubscribeToValues()
{
yield return MyValue1.Subscribe(
value => DoSomething1(value),
ex => Log.Error(ex, ex.Message),
() => OnCompleted());
yield return MyValue2.Subscribe(
value => DoSomething2(value),
ex => Log.Error(ex, ex.Message),
() => OnCompleted());
}
private void DoSomething1(string value){ /* ... */ }
private void DoSomething2(string value){ /* ... */ }
private void OnCompleted() { /* ... */ }
_
IDisposable
を次のように実装します。
_ #region IDisposable
private ~MyViewModel()
{
Dispose(false);
}
public void Dispose()
{
Dispose(true);
}
private bool _isDisposed;
private Dispose(bool disposing)
{
if(_isDisposed) return; // prevent double disposing
// dispose values first, such that they call
// the onCompleted() delegate
MyValue1.Dispose();
MyValue2.Dispose();
// dispose all subscriptions at once
Subscriptions.Dispose();
// do not suppress finalizer when called from finalizer
if(disposing)
{
// do not call finalizer when already disposed
GC.SuppressFinalize(this);
}
_isDisposed = true;
}
#endregion
}
_
.AddRange()
メソッドを取得する拡張クラスは次のとおりです。
_public static class CollectionExtensions
{
public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> values)
{
foreach(var value in values)
{
collection.Add(value);
}
}
}
_
こちらもご覧ください
BooleanDisposable
は、オブジェクトがすでに破棄されているかどうかを照会できますCancellationDisposable
はBooleanDisposableに似ていますが、キャンセルトークンがありますContextDisposable
を使用すると、特定のスレッドコンテキストで破棄できます。MultipleAssignmentDisposable
は、古い使い捨てを廃棄せずに、ある使い捨てを別の使い捨てに置き換えますSerialDisposable
は、古い使い捨てを廃棄する際に、古い廃棄物を別のものと交換しますSingleAssignmentDisposable
は、別の使い捨てと交換できない使い捨てを保存しますこれらの答えの多くには次のようなものがあります...
public static void DisposeAll(this IEnumerable clx) {
foreach (Object obj in clx)
{
IDisposable disposeable = obj as IDisposable;
if (disposeable != null)
disposeable.Dispose();
}
}
usersCollection.DisposeAll();
usersCollection.Clear();
.Clear()が役立つ理由を説明する単一の答えはありません。答えは、コレクション内のアイテムをコレクションと互いの両方から切り離すことです。
オブジェクトの分解時に切り離すほど、ガベージコレクターが適時にジョブを実行する可能性が高くなります。多くの場合、.NETメモリリークは、99%が未使用で、まだ参照されている1つのアイテムを持つオブジェクトの大きなグラフから発生します。
私はその良い習慣だと思う...
... IDisposableを実装するクラス内。
すべてにIDisposableを実装してこれを行うことを提案しているわけではありません。もしそうなったら、廃棄を実装する必要があるかもしれないと言っています。オブジェクトが...
オブジェクトを切り離すためだけにDisposeを実装するのは、メモリリークがあり、メモリプロファイラの分析がそれが役立つ可能性があることを知っているときだけです。
コレクションに対するforeachループ内のオブジェクトの破棄を呼び出す多くの答えを見ます。 Disposeは、ガベージコレクターの次回実行時に削除するオブジェクトにマークを付けるだけなので、正常に動作します。ただし理論的には、アイテムを破棄するとコレクションが変更され、foreachが破損する可能性があるため、最初にそれらの使い捨てオブジェクトを収集し、元のリストをクリアし、forまたはwhileループ内で末尾から開始して削除を呼び出す方がより堅牢です各反復のオブジェクト、たとえば次のメソッドを呼び出します。
public static void DisposeItemsInList<T>(this IList<T> list) where T : IDisposable
{
DeleteItemsInList(list, item => item.Dispose());
}
public static void DeleteItemsInList<T>(this ICollection<T> list, Action<T> delete)
{
if (list is IList && !((IList)list).IsFixedSize)
{
while (list.Count > 0)
{
T last = list.Last();
list.Remove(last);
delete?.Invoke(last);
}
}
else
{
for (int i = 0; i < list.Count; i++)
{
delete?.Invoke(list.ElementAt(i));
}
}
}
私は実際にDeleteItemsInListを他の目的に使用しています、例えばファイルを削除するには:DeleteItemsInList(File.Delete))
それらが述べたように、一般的な場合、そのようなリストを処分する必要はないはずです。アイテムをリストに配置する場合、Streamを使用し、いくつかのストリームを収集し、それらからデータを変換します。その後、これらのストリームを破棄し、変換後のオブジェクトのみを処理のために保持します。
誰もがGCに任せると述べているように、これが最善の選択肢であり、GCを強制しないでください。変数をnullに設定すると、GCの変数がマークされます。
あなたの後にもっと情報があれば: C#でガーベッジコレクションを強制するためのベストプラクティス
私は、大量のデータが処理されているときに、コレクションが範囲外になるまでGCがクリーンアップしないシナリオに遭遇しました(技術的には、GCは適切であると判断したときにコレクションを実行しますが、その後、コレクションが範囲外になります)。
これらの(まれな)シナリオでは、次のクラスを使用しました。
public class DisposableList<T> : List<T>, IDisposable
{
public void Dispose()
{
}
}
その後、通常のリストと同じように使用できます。
var myList = new DisposableList<MyObject>();
終了したら、Disposeメソッドを呼び出します。
myList.Dispose();
または、あるいは、usingステートメントで宣言します。
using (var myList = new DisposableList<MyObject>())
{
...
}
これにより、DisposableListが範囲外になるか破棄されると、GCはすぐにコレクションを実行します。
なぜリストを破棄したいのですか?参照がなくなった場合、GCが自動的に実行します。
ガベージコレクション: msdn.Microsoft.com/en-us/library/0xy59wtx.aspx
この質問をしている場合、メモリ不足の例外が発生したことを願っています。そうでない場合は、メモリ不足の例外を引き起こすテストを作成する必要があります。
実際にメモリの問題があると仮定すると、Userオブジェクト内のすべてがメモリを消費しているものを特定する必要があります。ユーザーオブジェクトのプロパティをnullに設定します。これは、ほとんどのメモリを消費します。
User.BigData = null;
その後、ユーザーのリストを保持し、ガベージコレクターがすべてのメモリを消費しているプロパティをクリーンアップできるようにします。
リスト内のアイテムが管理されていないオブジェクトである場合、すべてのオブジェクトでDispose()を繰り返して呼び出すことができます。
foreach(User user in userCollection)
{
user.Dispose();
}
リストオブジェクトが管理対象オブジェクトの場合、何もする必要はありません。 GCが面倒を見てくれます。
もう1つのアイデアは、保持する変数のスコープを含むブラケットを使用することです。
例えば。
void Function()
{
... some code here ....
{ // inside this bracket the usersCollection is alive
// at the end of the bracet the garbage collector can take care of it
List<User> usersCollection =new List<User>();
User user1 = new User();
User user2 = new User()
userCollection.Add(user1);
userCollection.Add(user2);
foreach(User user in userCollection)
{
}
}
... other code here ....
}