2012-04-21 3 views
5

Что такое синтаксис для возврата события из функции? (Не для вызова события, чтобы вернуть его, чтобы он мог привязываться к функциям).Возврат события из функции

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

Цель состоит в том, чтобы иметь возможность написать что-то вроде этого:

класс
Container c = new Container(); 
c.CreateEventForKey("a");    // Create the member in the dictionary 
c.EventForKey("a") += some_function; // Bind some_function to the event in the "a" member 
c.OnEventForKey("a","b");    // Calls some_function with argument "b" 

Контейнер выглядит следующим образом:

public class Container { 

    public class Member { 
    public event Action<string> AnEvent; 
    public void OnEvent(string v) { if(AnEvent!=null) { AnEvent(v); } } 
    } 

    protected Dictionary<string,Member> members; 

    // This seems to work OK. 
    public void OnEventForKey(string k, string v) { 
    if (members.ContainsKey(k)) { members[k].OnEvent(v); } 
    else { /* report error */ } 
    } 

    // Can't get this to compile. 
    public event Action<string> EventForKey(string k) { 
    if (members.ContainsKey(k)) { return members[k].AnEvent; } 
    else { /* report error */ } 
    } 
} 

Как я могу определить EventForKey так, что это делает то, что я могу рассчитывать?

+1

Я не думаю, что вы можете вернуть событие, но вы можете вернуть делегат, который может быть связан с событием. Это то, что вы имели ввиду? –

+1

Я думаю, вам просто нужно удалить ключевое слово события из объявления AdEvent, а также из возвращаемого типа EventForKey. –

+0

@IanNewson - ключевая часть того, с чем у меня возникают проблемы, - это 'c.EventForKey (" a ") + = some_function;'. Я бы предпочел написать это, чем громоздкие 'c.members [" a "]. AnEvent + = some_function' (как я могу добавить проверку ошибок и протоколирование к первой версии, но не ко второй). –

ответ

8

Что такое синтаксис для возврата события из функции?

Вы не можете, легко. События - подобные свойства - не являются действительно первоклассными «объектами» как таковыми; они являются членами класса. У вас действительно нет есть член класса здесь - вы пытаетесь просто сохранить делегатов в словаре.

Возможно, создать свой собственный «event-like» контейнер, но, вероятно, лучше рассмотреть альтернативные проекты, например.

c.Subscribe("a", SomeFunction); 
c.OnEventForKey("a"); 

Возможно, вы захотите посмотреть на EventHandlerList для вдохновения.

0

Почему бы не просто вернуть участника и подписаться на него?

public IMember MemberForKey(string key) // return IMember 
{ 
    if (!members.ContainsKey(key)) 
     throw new Exception(); 

    return members[key]; 
} 

А потом подписаться:

Container c = new Container(); 
c.CreateEventForKey("a");    
c.MemberForKey("a").AnEvent += some_function; 
c.OnEventForKey("a", "b"); 

Но у вас есть общественный OnEvent метод в Member классе. Чтобы запретить сбор событий клиентом, вы можете создать интерфейс, который будет показывать только событие. Просто реализовать этот интерфейс по Member класса:

public interface IMember 
{ 
    event Action<string> AnEvent; 
} 

И да, вы не можете вернуться событием, потому что на самом деле событие не является объектом, то есть совокупность двух методов add и remove, которые добавляют и удаляют делегат внутреннего поля делегата тип. Вот как выглядит ваше событие:

private Action<string> _action; // field of delegate type 

    public event Action<string> AnEvent 
    { 
     add { _action += value; } 
     remove { _action -= value; } 
    } 

Цель мероприятия - предоставить только две операции для клиентов - добавление и удаление обработчиков. Сам делегат скрыт от клиентов. Вы можете сделать это общедоступным:

public Action<string> _action; 

Но в этом случае любой клиент может его вызвать.

UPDATE: если вы хотите, чтобы пойти с Подписаться/Удалить синтаксис, а затем просто использовать словарь с обработчиками:

public class Container 
{ 
    private Dictionary<string, Action<string>> handlers = 
      new Dictionary<string, Action<string>>(); 

    public void CreateEventForKey(string key) 
    { 
     // with empty handler added you can avoid null check 
     handlers.Add(key, (value) => { }); 
    } 

    public void OnEventForKey(string key, string value) 
    { 
     if (!handlers.ContainsKey(key)) 
      throw new Exception(); 

     handlers[key](value); 
    } 

    public void Subscribe(string key, Action<string> handler) 
    { 
     if (!handlers.ContainsKey(key)) 
      throw new Exception(); 

     handlers[key] += handler; 
    } 
} 
0

Вот полный рабочий пример:

class Program 
{ 
    static void Main(string[] args) 
    { 
     Container c = new Container(); 
     c.CreateEventForKey("a");    // Create the member in the dictionary 
     c.EventForKey("a").Add(str => Console.WriteLine(str)); 
     c.EventForKey("a").Add(str => Console.WriteLine(str.ToUpper())); 
     c.OnEventForKey("a", "baa baa black sheep"); 

     Console.ReadLine(); 
     } 
    } 

    public class Container 
    { 

     public class Member 
     { 
     public List<Action<string>> AnEvent = new List<Action<string>>(); 
     public void OnEvent(string v) 
     { 
      if (AnEvent != null) 
      { 
       this.AnEvent.ForEach(action => action(v)); 
      } 
     } 

     public void AddEvent(Action<string> action) 
     { 
      this.AnEvent.Add(action); 
     } 
    } 

    protected Dictionary<string, Member> members = new Dictionary<string,Member>(); 

    public void CreateEventForKey(string key) 
    { 
     this.members[key] = new Member(); 
    } 

    // This seems to work OK. 
    public void OnEventForKey(string k, string v) 
    { 
     if (members.ContainsKey(k)) { members[k].OnEvent(v); } 
     else { /* report error */ } 
    } 

    public List<Action<string>> EventForKey(string k) 
    { 
     if (members.ContainsKey(k)) { return members[k].AnEvent; } 
     else { throw new KeyNotFoundException(); } 
    } 
} 

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

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