web-dev-qa-db-ja.com

リストを破棄する最良の方法

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;
}

どちらがベストですか?

29
Vivekh

最善のアイデアは、ガベージコレクターに任せることです。参照のみがリストの要素ではなくforeachに設定されるため、nullは何もしません。リストをnullに設定すると、実際にはガベージコレクションが発生する可能性があります(この投稿を参照してください C#:オブジェクト変数をnullに割り当てる必要がありますか? )。

23
Cornelius

第一に、リストはIDisposableではないためリストを「破棄」することはできません。また、リストを強制的に収集することはできませんC#が機能しないため通常実行しますnothingこちら。 mightを行う必要がある場合anything

  • メソッド変数であり、メソッドがすぐに終了する場合は、何もしないでください。メソッドが存在した後のある時点でGCに心配させてください。
  • それがフィールド(インスタンス変数)であり、オブジェクトがすぐにスコープから外れる場合は、何もしないでください。インスタンスに到達できない後のある時点でGCに心配させてください。

anythingが必要なのは、フィールド(またはcaptured variable/iterator block variable/etc)の場合だけですandインスタンス(/ delegate/iterator)は長生きします-その後、おそらくリストフィールドをnullに設定します。ただし、他のコードがまだリストへの参照を持っている場合、すべてが到達可能であることに注意してください。

18
Marc Gravell

リスト内のオブジェクトが不要になった場合、何もすべきではないことに同意しません。オブジェクトがインターフェース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を実装していない場合でも使用できます。

15

この投稿の別のアイデア...コレクションのすべてのメンバーを適切に破棄したい場合は、次の拡張メソッドを使用できます。

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();

これにより、すべてのメンバーにリソースを解放する機会が与えられ、結果のリストが空になります。

12
jheddings

十分なコンテキストが提供されていません。ここではスコープが重要です。

GCは、nullに設定することなく、ユーザーとコレクションに割り当てられたメモリを処理するのに十分スマートであると思います。

コレクションがコレクションから必要のないユーザーを削除し、他のオブジェクトがそれらを参照しない場合、ヒントを提供することなく、GCされます。

オブジェクトへのライブ参照がある限り、GCはオブジェクトをクリーンアップしません。すべての参照を削除し、その仕事をすることができます。

2
duffymo

IDisposableインターフェイスを実装するオブジェクトのリストを破棄するために使用できる拡張メソッドのさらに別の例。これはLINQ構文を使用します。

    public static void Dispose(this IEnumerable collection)
    {
        foreach (var obj in collection.OfType<IDisposable>())
        {
            obj.Dispose();
        }
    }
2
mslissap

最善の方法は

userCollection= null;

GCが休息の世話をします。

1
Pranay Rana

動作する一般的な実装(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();
}
1
Mohsen Afshin

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は、別の使い捨てと交換できない使い捨てを保存します
1
MovGP0

これらの答えの多くには次のようなものがあります...

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つのアイテムを持つオブジェクトの大きなグラフから発生します。

私はその良い習慣だと思う...

  • オブジェクトがサブスクライブされているイベントのサブスクライブを解除します
  • 開催されたコレクションをクリアする
  • すべてのメンバーをnullに設定します。

... IDisposableを実装するクラス内。

すべてにIDisposableを実装してこれを行うことを提案しているわけではありません。もしそうなったら、廃棄を実装する必要があるかもしれないと言っています。オブジェクトが...

  • イベントの購読または
  • dB接続やビットマップなどのDispose()を実装するメンバーを所有している、または
  • 他のアンマネージリソースがあります。

オブジェクトを切り離すためだけにDisposeを実装するのは、メモリリークがあり、メモリプロファイラの分析がそれが役立つ可能性があることを知っているときだけです。

1
Mick

コレクションに対する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を使用し、いくつかのストリームを収集し、それらからデータを変換します。その後、これらのストリームを破棄し、変換後のオブジェクトのみを処理のために保持します。

0
EricBDev

誰もがGCに任せると述べているように、これが最善の選択肢であり、GCを強制しないでください。変数をnullに設定すると、GCの変数がマークされます。

あなたの後にもっと情報があれば: C#でガーベッジコレクションを強制するためのベストプラクティス

0
user831055

私は、大量のデータが処理されているときに、コレクションが範囲外になるまで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はすぐにコレクションを実行します。

0
user2746082

なぜリストを破棄したいのですか?参照がなくなった場合、GCが自動的に実行します。

ガベージコレクション: msdn.Microsoft.com/en-us/library/0xy59wtx.aspx

0
Patrick

この質問をしている場合、メモリ不足の例外が発生したことを願っています。そうでない場合は、メモリ不足の例外を引き起こすテストを作成する必要があります。

実際にメモリの問題があると仮定すると、Userオブジェクト内のすべてがメモリを消費しているものを特定する必要があります。ユーザーオブジェクトのプロパティをnullに設定します。これは、ほとんどのメモリを消費します。

User.BigData = null;

その後、ユーザーのリストを保持し、ガベージコレクターがすべてのメモリを消費しているプロパティをクリーンアップできるようにします。

0

リスト内のアイテムが管理されていないオブジェクトである場合、すべてのオブジェクトでDispose()を繰り返して呼び出すことができます。

foreach(User user in userCollection)
{
user.Dispose();
}

リストオブジェクトが管理対象オブジェクトの場合、何もする必要はありません。 GCが面倒を見てくれます。

0
RJN

もう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 ....
}
0
Aristos