2010-11-30 8 views
4

나는 List<Action<int>>을 생성하고 나중에이 클래스를 보유하는 클래스가 있습니다. 이 클래스는이 목록에서 대리인을 추가 및 제거 할 수 있습니다. 이것은 사람들이 너무 공상에 빠지지 않는 한 잘 작동합니다. 제거 할 수없는 익명 함수를 처리하기 위해 위임 대상이 null 인 지 확인합니다. null 인 경우 예외가 발생합니다. 이 문제는 함수가 포함 된 익명의 대리자가있을 때 발생합니다. 이것은 목표를 가지고 있지만 마찬가지로 제거 할 수 없습니다. 단순화 된 코드는 아래 내가익명 함수를 식별하는 방법

p => { fake.Test(p); counter++; } 

누군가가 그것을 시도하는 경우 내가 던질 수있는 익명 함수입니다 식별 할 수있는 방법이 필요합니다 내 문제

public class MyDelegateContainer 
{ 
    List<Action<int>> m_Container = new List<Action<int>>(); 

    public void Add(Action<int> del) 
    { 
     if (del.Target == null) 
     { 
      throw new Exception("No static handlers"); 
     } 
     m_Container.Add(del); 
    } 

    public bool Remove(Action<int> del) 
    { 
     if (m_Container.Contains(del)) 
     { 
      m_Container.Remove(del); 
      return true; 
     } 

     return false; 
    } 
} 

public class MyFakeActionClass 
{ 
    public void Test(int temp) { } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     bool removed = false; 
     int counter = 0; 
     MyDelegateContainer container = new MyDelegateContainer(); 
     MyFakeActionClass fake = new MyFakeActionClass(); 
     //container.Add(p => { }); //Throws, this is what I want to happen 
     container.Add(fake.Test); //Works, this is the use case 
     removed = container.Remove(fake.Test); //Works, this is the use case 
     Debug.Assert(removed); 
     container.Add(p => { fake.Test(p); counter++; }); //Works but I would like it not to 
     removed = container.Remove(p => { fake.Test(p); counter++; }); //doesn't work 
     Debug.Assert(removed); 
    } 
} 

보여줍니다. 어떤 도움 주셔서 감사합니다

편집 : 익명 함수에 대해 변수 Action<int>을 사용할 수 있으며 모든 것이 작동하지만 추가 및 제거가 결코 실제로 같은 범위에 있지 않음에 유의해야합니다.

+0

식별되면 익명으로 처리되지 않습니다. 대신에 클로저를 찾으십니까? –

+0

익명의 기능을 감지하는 방법이 필요합니다 – Steve

+0

@Steve - 83 개의 답변 중 28 개가 답답합니다! O_O 그리고 나는 단지 당신이 좋은 점수를 가지고 있다고 말하고 있습니다.) – BeemerGuy

답변

3

모든 함수의 이름이 CLR이므로 함수가 "익명"인지 여부를 신뢰할 수있는 방법으로 확인할 수는 없습니다. 그것은 그것을 생성하는 언어 내에서만 익명이고 컴파일러에 따라 다릅니다. Microsoft의 현재 C# 컴파일러에서 사용하는 알고리즘을 결정할 수 있으며 C# 5 또는 Mono에서 작동하지 않을 수 있습니다.

유형을 사용하는 사용자가 잘못 사용하는 코드를 작성하지 못하도록하려면 일부 예외 (예 :)를 던져 프로그램을 중단해야합니다. 대상 대리자를 찾을 수 없으면 Remove 함수에서 예외를 throw합니다. 이 시점에서 사용자는 여전히 충돌을 일으키지 만이를 해결할 수있는 유일한 방법은 제거 할 수있는 방법으로 델리게이트를 작성하는 것입니다.

추가 보너스로 누군가가 위임자를 두 번 제거하려고 시도하거나 처음에 추가되지 않은 버그를 잡을 수 있습니다.코드는 다음과 같을 것이다 :

public class MyDelegateContainer 
{ 
    Dictionary<string, Action<int>> m_Container = 
     new Dictionary<string, Action<int>>(); 

    public void Add(string key, Action<int> del) 
    { 
     m_Container.Add(key, del); 
    } 

    public bool Remove(string key) 
    { 
     return m_Container.Remove(key); 
    } 
} 

그런 다음 당신은 쉽게 어떤 임의의로 알려진 대리자를 제거 할 수 있습니다 :

public bool Remove(Action<int> del) 
{ 
    if (m_Container.Contains(del)) 
    { 
     m_Container.Remove(del); 
     return true; 
    } 

    throw new ArgumentException("Attempt to remove nonexistent delegate"); 
} 
+0

지금까지이 방법이 가장 좋은 방법 인 것 같습니다. 가기. – Steve

+0

+1 컴파일러 종속적 인 설명입니다. –

1

인트로 스펙 션을 사용하여 메소드의 이름을 확인합니다.

익명 메소드는 일반적으로 매우 예측 가능한 이름을 갖습니다. (정확한 형식은 기억이 나지 않지만 몇 가지 테스트를 실행하면 분명합니다.)

누군가가 익명이 아닌 방법을 만들었지 만 anonMethod123 (또는 형식이 ...이든간에) 이름을 정하면 단점이 있습니다.

+4

잘 작동합니다. 컴파일러가 변경 될 때까지 – Gabe

0

물론 익명 메소드를 제거 할 수 있습니다. 익명 메소드를 참조하기 만하면됩니다.

var myAnonymousMethod = p => { fake.Test(p); counter++; }; 
container.Add(myAnonymousMethod); 
removed = container.Remove(myAnonymousMethod); 
+0

죄송합니다. 제 편집문은 이러한 것들이 일반적으로 동일한 범위에서 발생하지 않을 것이며, 단지 그것을 완전히 막고 싶다고 말했습니다. – Steve

+0

@ 스티브, 언어의 사용을 제한해야하는 정말로 강력한 이유가 있어야합니다. 예를 들어 이벤트를 살펴보면 나중에 가입을 취소하지 않으려는 경우 사용자에게 익명으로 가입 할 수 있습니다. 제거 할 기존 대리자를 검색하는 방법을 제공하는 등 다른 방법을 찾아보십시오. –

4

예를 들어 호출자는 처리기를 제거해야합니다. 따라서 호출자가 핸들러를 제거하지 않으려는 경우 처리기가 익명의 대리자/람다인지 여부에 관계없이 제거되지 않습니다.

은 호출자가 대신 컨테이너에 다시 핸들러를 통과, 여전히 핸들러를 제거 할 책임이

public class MyDelegateContainer 
{ 
    List<Action<int>> m_Container = new List<Action<int>>(); 

    public Action Add(Action<int> del) 
    { 
     m_Container.Add(del); 

     return new Action(() => 
     { 
      m_Container.Remove(del); 
     }); 
    } 
} 
은, 그것이 "토큰을 수신 :

나의 제안은 다음과 같이에 위임 컨테이너를 변경하는 것입니다 "나중에 저장하여 처리기를 제거 할 수 있습니다.

+1

아마도이 문제에 직면하면 제가 갈 수있는 방법 일 것입니다. – jonnii

+0

불행히도, 이것은 매우 오래 살아있는 수업이며, 나는 그들을 제거하지 않는 사람들과 괜찮습니다. 난 그냥 수시로 동적으로 처리기를 제거 할 수 있어야합니다. – Steve

+2

이것은 약간 무의미한 것 같습니다. 처음에 원래 대리인을 저장할 수있을 때 왜 토큰을 저장해야합니까? – Gabe

0

jonnii 코멘트에서 제안한 것처럼, 당신이 그것을 구현할 수있는 또 다른 방법은 사전 함께

container.Add("fake.Test", fake.Test); 
    removed = container.Remove("fake.Test"); 
    Debug.Assert(removed); 
    container.Add("anon", p => { fake.Test(p); counter++; }); 
    removed = container.Remove("anon"); // works! 
    Debug.Assert(removed); 
0

오래된 질문 내가 알고 있지만 방법이 있다면이 검사의 카드 키 현재 (미래의) 방법이 될 것이라고 생각 : 그냥 뭐 이름을 아시는하여 코드의 포인트를 추가하는 데 사용 된 익명 :

bool isAnonymous = !System.CodeDom.Compiler.CodeGenerator.IsValidLanguageIndependentIdentifier(del.Method.Name); 

익명 메소드의 런타임 이름이 충돌하지 않도록 컴파일시에 사용하는 경우 무효로 할 것입니다.

관련 문제