Re: как удалить из события все делегаты?
От: _FRED_ Черногория
Дата: 03.10.06 16:45
Оценка: +3
Здравствуйте, <Аноним>, Вы писали:

А>есть класс в котором набор событий.

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


Если класс, от событий которого надо отписаться, разрабатываешь ты сам и не страшно добавить в него некоторую для этого функциональность (судя по-примерам, такое возможно), то почему бы не сделать так:
namespace ConsoleApplication1
{
  using System;
  using System.Diagnostics;

  class Program
  {
    static void Main(string[] args) {
      Test t = new Test();
      t.Event += delegate { Debug.Print("Test Handler!"); };
      t.FireEvent();
      t.ClearEvent();
      t.FireEvent();
    }
  }

  class Test
  {
    public event EventHandler Event;

    internal void FireEvent() {
      if(Event != null) {
        Event(this, EventArgs.Empty);
      }//if
    }

    internal void ClearEvent() {
      Event = null;
    }
  }
}
... << RSDN@Home 1.2.0 alpha rev. 652>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
Re[2]: А еще можно так... :)
От: _FRED_ Черногория
Дата: 04.10.06 07:28
Оценка: +1
Здравствуйте, ppti, Вы писали:

P>Есть ведь способ написать одну процедуру для всех событий сразу?

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

P>как это не смешно, но дальше ступор и не понимаю где взять ссылку на найденное событие
P>и ссылку на делегат, который в этом событии есть. Не возможность запустить делегат события методом
P>invoke, а подствить его в конструкцию
P>Event -= eh


В том-то и песня, что событие — EventInfo (как и свойство-PropertyInfo и метод-MethodInfo) — это (я своими словами, уж как умею ) интерфейсная надстройка, только лишь декларация некоей операции. В подовляющем большинстве случаев событие (*) связано с изменением поля класса, в котором оно (событие) объявлено. Это поле (**) имеет тип делегата, того же, что задан при объявлении события. (Как, например, свойство связано с изменением некоторого поля класса).

Проблема из-за того, что декларативно нигде не указана связь между событием (*) и полем-делегатом (**) (как, например, между свойством и полем, которое оно представляет) и из-за этого _невозможно решить твою задачу тем путём, что ты привёл (по имени события обнулить делегат), если не знать заранее, каким образом и где класс хранит подписчиков.

Например, если в моём примере класса Test объявить событие так:
    public event EventHandler Event {
      add { }
      remove { }
    }

то поле делегата (**), о котором я говорил, не будет создано компилятором и заранее вообще невозможно предугадать, что присходит при подписке и отписки от события.

Правда, есть возможность решить задачу отписки от всех событий некоего класса, если этот класс работает с событиями так, как, например, System.ComponentModel.Component — с помощью System.ComponentModel.EventHandlerList. Тогда, добравшись до свойства, наподобии Component.Events (что возможно через Reflection) надо у того (у этого свойства, до которого добрались) вызвать Dispose().
... << RSDN@Home 1.2.0 alpha rev. 652>>
Now playing: «Тихо в лесу…»
Help will always be given at Hogwarts to those who ask for it.
как удалить из события все делегаты?
От: Аноним  
Дата: 03.10.06 16:29
Оценка:
Cитуация такова.

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

RemoveAllHandlerFromEvent(string ANameEvent)
{
  // удаляем из события по имени ANameEvent
  // все назначенные обработчики
}


или

RemoveAllHandlerFromEvent(Delegate ANameEvent)
{
  // удаляем из события ANameEvent
  // все назначенные обработчики
}



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


у меня было два варианта
1. через Reflection получить список EventInfo[], далее получить доступ
к событию и список MethodInfo[]
однако, получив скажем событие Е через Reflection, а должен буду
написать
E -= M; //M = метод который может быть назначен на событие..
но КАК получить это через Reflection непонятно..
вызвать методы можно через вызов Invoke, а удалить -= нельзя..

плюс такого подхода в том что он универален и позволяет в любом классе
удалить все назначенные методы

2. в описании событий использовать дополнительный список для хранения
всех делегатов примерно вот так
(за точный синтаксис не ручаюсь, но идейно так):

private List<EventHandler> FMyList;
public event EventHandler FMyEvent
add
{
  FMyEvent += value;
  FMyList.Add(value);
}
remove
{
  FMyEvent -= value;
  FMyList.remove(value); 
}



работать будет думаю, но придется перелопатить все события в исходном классе.
некрасиво..

какие будут предложения?
Re[2]: как удалить из события все делегаты?
От: Аноним  
Дата: 03.10.06 17:55
Оценка:
Здравствуйте, _FRED_, Вы писали:

А>>Если класс, от событий которого надо отписаться, разрабатываешь ты сам и не страшно добавить в него некоторую для этого функциональность (судя А>>по-примерам, такое возможно), то почему бы не сделать так:


Точно! можно же просто занулить событие.. !
а я все от с++ и дельфи не могу отойти.. если создал — то нужно обязательно удалить.
спасибо!
А еще можно так... :)
От: Streamer1 Украина  
Дата: 03.10.06 22:02
Оценка:
Здравствуйте, <Аноним>, Вы писали:

А>Здравствуйте, _FRED_, Вы писали:


А>>>Если класс, от событий которого надо отписаться, разрабатываешь ты сам и не страшно добавить в него некоторую для этого функциональность (судя А>>по-примерам, такое возможно), то почему бы не сделать так:


А>Точно! можно же просто занулить событие.. !

А>а я все от с++ и дельфи не могу отойти.. если создал — то нужно обязательно удалить.

а можно еще так:
using System;
using System.Diagnostics;

class Program
{
  static void Main(string[] args) 
  {
    Test t = new Test();
    t.Event += delegate { Console.WriteLine("Test Handler!"); };
    t.FireEvent();
    t.ClearEvent();
    t.FireEvent();
  }
}

class Test
{
  public event EventHandler Event;

  internal void FireEvent() 
  {
    if(Event != null) 
    {
      Event(this, EventArgs.Empty);
    }//if
  }

  internal void ClearEvent() 
  {
    foreach(EventHandler eh in Event.GetInvocationList())
       Event -= eh;
  }
}
Тот кто знает не говорит, тот кто говорит не знает.
Re: А еще можно так... :)
От: ppti  
Дата: 04.10.06 05:28
Оценка:
Здравствуйте, Streamer1, Вы писали:

S>а можно еще так:

S>
S>  internal void ClearEvent() 
S>  {
S>    foreach(EventHandler eh in Event.GetInvocationList())
S>       Event -= eh;
S>  }
S>}
S>


согласен, но это заставляет перелопатить весь код с событиями.
Есть ведь способ написать одну процедуру для всех событий сразу?

типа

//получаем событие по строке
public void RemoveHandlersFromEvent(string ANameEvent)
{
    //получаем список всех событий
    Type t = typeof(TOLAPGrid);
    EventInfo[] EI = t.GetEvents();
    foreach (EventInfo E in EI)
    {
         // нашли нужное событие
         if (E.Name == ANameEvent) 
         {
             //могу удалить делегата из события:
             E.RemoveEventHandler( событие, делегат);
             //могу получить метод для удаления методов: 
             MethodInfo MI = E.GetRemoveMethod();
             //и вызвать его
             MI.Invoke(this, new object[1]);
         }
    }

}


как это не смешно, но дальше ступор и не понимаю где взять ссылку на найденное событие
и ссылку на делегат, который в этом событии есть. Не возможность запустить делегат события методом
invoke, а подствить его в конструкцию
Event -= eh
хорошо там где нас нет. но мы есть везде.
Re[2]: А еще можно так... :)
От: desco США http://v2matveev.blogspot.com
Дата: 04.10.06 07:27
Оценка:
Здравствуйте, ppti, Вы писали:

В случае, если мы имеем дело с field-like events, можно поступить так, как здесь
Автор: _FRED_
Дата: 03.10.06
предложил _FRED_, только используя рефлекшн

  class TestClass
  {
    public static event EventHandler StaticEvent;
    public event EventHandler InstanceEvent;
    
    static public void RaiseStaticEvent()
    {
      if (StaticEvent != null)
      {
        StaticEvent(null, EventArgs.Empty);
      }
      else
      {
        Console.WriteLine("Empty invocation list");
      }
    }

    public void RaiseInstanceEvent()
    {
      if (InstanceEvent != null)
      {
        InstanceEvent(null, EventArgs.Empty);
      }
      else
      {
        Console.WriteLine("Empty invocation list");
      }
    }    
    
    public static void RemoveAllEvents(object instance, EventInfo eventInfo)
    {
      Type rt = instance != null ? instance.GetType() : eventInfo.ReflectedType;
      BindingFlags bf = BindingFlags.NonPublic;
      bf |= instance != null ? BindingFlags.Instance : BindingFlags.Static;
      FieldInfo fi = rt.GetField(eventInfo.Name, bf);
      if (fi != null)
      {
        fi.SetValue(instance, null);
      }      
    }
  }

  public class Program
  {
    static void Main(string[] args)
    {
      TestClass.StaticEvent += delegate
                                 {
                                   Console.WriteLine("Static event");
                                 };
      TestClass instance = new TestClass();
      instance.InstanceEvent += delegate
                                  {
                                    Console.WriteLine("Instance event");
                                  };
      
      TestClass.RaiseStaticEvent();
      instance.RaiseInstanceEvent();
      
      TestClass.RemoveAllEvents(null, typeof(TestClass).GetEvent("StaticEvent"));
      TestClass.RemoveAllEvents(instance, typeof(TestClass).GetEvent("InstanceEvent"));

      TestClass.RaiseStaticEvent();
      instance.RaiseInstanceEvent();
      
    }
  }



вывод:

Static event
Instance event
Empty invocation list
Empty invocation list

Re[3]: А еще можно так... :)
От: Pavel M. Россия  
Дата: 04.10.06 07:39
Оценка:
Здравствуйте, desco, Вы писали:

D>Здравствуйте, ppti, Вы писали:


В свое время тоже бился над этим вопросом. В приведенных выше примерах есть один недостаток, можно забыть отцепить обработчик и в итоге — утечка памяти. Мы обсуждали уже эту тему как-то, могу найти. Там предлагали решение на WealReference, однако, не очень удачное. Интересно, можно ли именно для этих целей придумать паттерн какой-нибудь на все случаи жизни...
--------------------------
less think — do more
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.