2010-11-30 2 views
4

У меня есть класс, который создает List<Action<int>> и держится за них до более позднего времени. Этот класс может добавлять и удалять делегаты из этого списка. Это работает хорошо, пока люди не слишком причудливы. Для борьбы с анонимной функцией (которая не может быть удалена) я проверяю, что цель делегата равна нулю. Если его значение null, я выдаю исключение. Проблема возникает, когда есть анонимный делегат, содержащий функцию. У этого есть цель, но такая же неустранима. Упрощена ниже код иллюстрирует мои вопросыКак идентифицировать анонимную функцию

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

мне нужен какой-то способ, чтобы определить

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

является анонимная функция, так что я могу бросить, если кто-то пытается его. Спасибо за любую помощь.

EDIT: Я должен отметить, что для анонимной функции я мог бы использовать переменную Action<int>, и все будет работать, но добавление и удаление никогда не будут в той же области на практике.

+0

Если это определено, это не является анонимным. Вы ищете закрытие вместо? K – 2010-11-30 19:45:30

+0

Мне нужно каким-то образом обнаружить, что его анонимная функция – Steve 2010-11-30 19:46:26

+0

@Steve - 28 вопросов без принятого ответа из 83 ?! O_O И я только говорю, что у вас есть хороший балл =) – BeemerGuy 2010-11-30 19:48:20

ответ

3

Невозможно надежно определить, является ли функция «анонимной», поскольку все функции имеют имена для CLR. Это только анонимный язык, который генерирует его, и это зависит от компилятора. Вы можете определить алгоритм, используемый текущим компилятором Microsoft C#, только чтобы он переставал работать на C# 5 или Mono.

Поскольку вы хотите, чтобы пользователи вашего типа не писали код, который использует его неправильно, вам просто нужно выбросить исключение в , что приведет к их сбою в работе программы. Я бы выбрал исключение в функции Remove, когда целевой делегат не найден. В этот момент ваши пользователи по-прежнему получат сбой, и единственный способ исправить это - написать делегат в некотором роде, что он является съемным.

В качестве дополнительного бонуса вы поймаете ошибки, когда кто-то попытается удалить делегатов дважды или которые никогда не были добавлены в первую очередь.Код будет выглядеть следующим образом:

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"); 
} 
1

Я бы использовал интроспекцию, чтобы проверить имена методов.

Анонимные методы обычно имеют очень предсказуемые имена. (Я не помню точный формат, но выполняю некоторые тесты, и это должно быть очевидно).

Недостатком было бы то, что если бы кто-нибудь создал не анонимный метод, но решил назвать его anonMethod123 (или независимо от формата ...) Это было бы ложно отвергнуто.

0

Конечно, вы можете удалить анонимный метод, вам просто нужно иметь ссылку на тот же анонимный метод.

var myAnonymousMethod = p => { fake.Test(p); counter++; }; 
container.Add(myAnonymousMethod); 
removed = container.Remove(myAnonymousMethod); 
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); 
     }); 
    } 
} 

Вызывающий по-прежнему отвечает за удаление обработчика, но вместо передачи обработчик снова в контейнер, он получает «маркер «что он может сохранить и использовать позже, чтобы удалить обработчик.

0

Как jonnii предложил в комментариях, еще один способ можно реализовать это со словарем:

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

Затем вы можете легко удалить известный делегат в некоторых произвольных точка в вашем коде, просто зная, что имя было использовано, чтобы добавить его:

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

Имя выполнения анонимного метода должно быть недействительным, если используется во время компиляции, чтобы убедиться, что он не конфликтовать.

Смежные вопросы