2016-10-12 11 views
1

デリゲートのスタックを管理するクラスがあるとします。何らかの理由で、ある時点で私はスタック上のランダムなデリゲートを無効にする必要があるので、デリゲートの変数を参照として渡すので、その変数をnullに設定するとスタックがそのメソッドを呼び出すのを防ぐそれはnullですが、代わりにメソッドへの参照がそこに残っているので、呼び出されます。ここで私が何を意味するか示すために、いくつかの愚かなコード:C#デリゲート参照を無効にする

public delegate void DlgtVoidVoid(); 

public class TestPile { 
    Pile pileStack = new Pile(); 
    DlgtVoidVoid _delvar1 = null; 
    DlgtVoidVoid _delvar2 = null; 
    public void Main() 
    { 
     _delvar1 = voidmethod1; 
     _delvar2 = voidmethod2; 
     pileStack.PushItem(ref _delvar1); 
     pileStack.PushItem(ref _delvar2); 
     //trying to invalidate the first delegate handler 
     _delvar1 = null; 
     pileStack.InvokeAndPop();//invoke 2 
     pileStack.InvokeAndPop();//shouldn't voidmethod1 call fail? 
     //but instead also voidmethod1 will be called: why? 
    } 
    void voidmethod1() 
    { 
     Console.WriteLine("calling void method one"); 
    } 
    void voidmethod2() 
    { 
     Console.WriteLine("calling void method two"); 
    } 
} 

、これはあくまでも一例のために少しスタックマネージャである:

public class Pile { 
    DlgtVoidVoid[] thepile = new DlgtVoidVoid[10]; 
    int pileindex = -1; 
    public void PushItem(ref DlgtVoidVoid item) 
    { 
     thepile[++pileindex] = item; 
    } 
    public DlgtVoidVoid PeekItem() 
    { 
     return thepile[pileindex]; 
    } 
    public DlgtVoidVoid PopItem() 
    { 
     return thepile[pileindex--]; 
    } 
    public void InvokeAndPop() 
    { 
     if(pileindex >= 0) { 
      if(PeekItem() != null) 
       PopItem().Invoke(); 
     } 
    } 
} 

C#ドンでREFと参照変数を渡すことは明らかです」私が期待しているように、C++やいくつかでは、C#でそれを達成する方法があるのでしょうか、あるいはそのコードロジックを変更する必要がありますか?

+1

'ref'は' PushItem'のローカル変数 'item'にのみ適用され、参照のコピーをスタックに格納しています。 '_delVar1'を' null'に設定すると、コピーされたスタック参照に影響しません。 – Lee

+0

ここで、 'PushItem()' by-refのためのパラメータを作成することは何のヒンジも行いません。あなたはまだ参照の*コピー*を 'thepile'に入れています。ここでいくつかの異なる論理を示唆します。 –

+0

デリゲートをNULLに設定すると何が期待されますか?おそらくエラーではありません... –

答えて

1

あなたのコードはあまりうまく設計されていません...ローカル変数をNULLに設定しても、他のクラスの内部データの変更は誰にも期待できません。たとえあなたがこれを達成することができたとしても、間違いなく遅かれ早かれバグにつながるでしょう。

クラスの配列の代わりにList<DlgtVoidVoid>を使用する方が良いでしょう。デリゲート参照をnullにするのではなく、の対応するメソッドを後で呼び出す特別なメソッド "Remove"を作成することをお勧めします。

public class Pile { 
    private List<DlgtVoidVoid> thepile = new List<DlgtVoidVoid>(); 

    public void PushItem(DlgtVoidVoid item) 
    { 
     thepile.Add(item); 
    } 
    public DlgtVoidVoid PeekItem() 
    { 
     return thepile[thepile.Count-1]; 
    } 
    public DlgtVoidVoid PopItem() 
    { 
     var item = thepile[thepile.Count-1]; 
     thepile.RemoveAt(thepile.Count-1); 
     return item; 
    } 
    public void InvokeAndPop() 
    { 
     PopItem()(); 
    } 

    public void Remove(DlgtVoidVoid deletageToRemove) { 
     thepile.Remove(deletageToRemove); 
    } 
} 

//... 

public void Main() 
{ 
    _delvar1 = voidmethod1; 
    _delvar2 = voidmethod2; 
    pileStack.PushItem(ref _delvar1); 
    pileStack.PushItem(ref _delvar2); 
    //trying to invalidate the first delegate handler 
    pileStack.Remove(_delvar1); 

    pileStack.InvokeAndPop();//invoke 2 
    pileStack.InvokeAndPop();//Now it will throw you IndexOutOfRange exception. You could handle it better inside of the Pile methods 
} 
+0

きれいでシンプルで、私はあなたの時間をありがとうございました。 – f4bo

0

参照渡しは、C++とほとんど同じです。つまり、呼び出し元の変数値を変更することができます。しかし、C++の場合と同じように、ポインターへのポインターを保存しない限り、値を後で変更することはできません。

あなたのコードでは、変数への参照ではなくオブジェクトを格納しているので、実際に配列から値を削除するには削除メソッドを呼び出す必要があります。変数を編集すると、そのオブジェクトへの他の参照では何も行われません。

もちろん、このデリゲートを含むオブジェクトを持つことができます。その外側のオブジェクトを変更すると、スタック内の内部デリゲートもnullになりますが、インダイレクションの別のレイヤーが追加され、削除メソッドを呼び出すよりも面倒です。

関連する問題