2010-12-07 2 views
20

У меня есть класс, EventContainer.cs, который содержит событие, скажем:Поднять событие класса из другого класса в C#

public event EventHandler AfterSearch; 

У меня есть еще один класс, EventRaiser.cs. Как я могу (и не обрабатывать) вышеупомянутое событие из этого класса?

Приведенное событие, в свою очередь, вызывает обработчик события в классе EventContainer. Что-то вроде этого (это, очевидно, неверно):

EventContainer obj = new EventContainer(); 
RaiseEvent(obj.AfterSearch); 
+0

Этих старый пост поднимает вопрос, который можно легко обойти с помощью концентратора событий, как [TinyMessenger ] (https://github.com/grumpydev/TinyMessenger). – Larry 2017-04-07 09:49:27

ответ

29

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

obj.AfterSearch += handler; (где обработчик - это метод согласно подписи AfterSearch). Один из них может подписаться на мероприятие извне просто отлично, но он может быть поднят только внутри класса, определяющего его.

+0

Спасибо Femaref для инсайдера. Постараюсь добиться каким-то другим способом. – samar 2010-12-09 05:28:27

+3

@Femaref: ** ВОЗМОЖНО **, попробуйте мой ответ ниже. – halorty 2011-10-05 11:01:22

+0

Имеет ли он практичность? Нет, это не так, в вашей первой строке сказано, что это взломать, если я найду что-то в исходном коде где-нибудь, я бы медленно закрыл окно и спокойно отработал, чтобы его больше не видели. – Femaref 2011-10-05 11:12:33

5

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

Конечно, это разрушение инкапсуляции и плохой дизайн.

2

Согласен с Femaref - и обратите внимание, что это важное различие между делегатами и событиями (см., Например, this blog entry, для хорошего обсуждения этого и других различий).

В зависимости от того, чего вы хотите достичь, вам может быть лучше с делегатом.

6

Похоже, вы используете Delegate pattern. В этом случае AfterSearch событие должно быть определено на EventRaiser класса, а EventContainer класс должен потреблять событие:

В EventRaiser.cs

public event EventHandler BeforeSearch; 
public event EventHandler AfterSearch; 

public void ExecuteSearch(...) 
{ 
    if (this.BeforeSearch != null) 
     this.BeforeSearch(); 

    // Do search 

    if (this.AfterSearch != null) 
     this.AfterSearch(); 
} 

В EventContainer.cs

public EventContainer(...) 
{ 
    EventRaiser er = new EventRaiser(); 

    er.AfterSearch += this.OnAfterSearch; 
} 

public void OnAfterSearch() 
{ 
    // Handle AfterSearch event 
} 
3

Существует хороший способ сделать это. Каждое событие в C# имеет делегат, который указывает знак методов для этого события. Определите поле в своем внешнем классе с типом делегата события. получите ссылку этого поля в конструкторе внешнего класса и сохраните его. В основном классе вашего мероприятия отправьте ссылку события для делегата внешнего класса. Теперь вы можете легко вызвать делегата в своем внешнем классе.

public delegate void MyEventHandler(object Sender, EventArgs Args); 

public class MyMain 
{ 
    public event MyEventHandler MyEvent; 
    ... 
    new MyExternal(this.MyEvent); 
    ... 
} 

public MyExternal 
{ 
    private MyEventHandler MyEvent; 
    public MyExternal(MyEventHandler MyEvent) 
    { 
      this.MyEvent = MyEvent; 
    } 
    ... 
    this.MyEvent(..., ...); 
    ... 
} 
14

Это ВОЗМОЖНО, но используя умный хак.

Вдохновленный http://netpl.blogspot.com/2010/10/is-net-type-safe.html

Если вы не верите, попробуйте этот код.

using System; 
using System.Runtime.InteropServices; 

namespace Overlapping 
{ 
    [StructLayout(LayoutKind.Explicit)] 
    public class OverlapEvents 
    { 
     [FieldOffset(0)] 
     public Foo Source; 

     [FieldOffset(0)] 
     public OtherFoo Target; 
    } 

    public class Foo 
    { 
     public event EventHandler Clicked; 

     public override string ToString() 
     { 
      return "Hello Foo"; 
     } 

     public void Click() 
     { 
      InvokeClicked(EventArgs.Empty); 
     } 

     private void InvokeClicked(EventArgs e) 
     { 
      var handler = Clicked; 
      if (handler != null) 
       handler(this, e); 
     } 
    } 

    public class OtherFoo 
    { 
     public event EventHandler Clicked; 

     public override string ToString() 
     { 
      return "Hello OtherFoo"; 
     } 

     public void Click2() 
     { 
      InvokeClicked(EventArgs.Empty); 
     } 

     private void InvokeClicked(EventArgs e) 
     { 
      var handler = Clicked; 
      if (handler != null) 
       handler(this, e); 
     } 

     public void Clean() 
     { 
      Clicked = null; 
     } 
    } 

    class Test 
    { 
     public static void Test3() 
     { 
      var a = new Foo(); 
      a.Clicked += AClicked; 
      a.Click(); 
      var o = new OverlapEvents { Source = a }; 
      o.Target.Click2(); 
      o.Target.Clean(); 

      o.Target.Click2(); 
      a.Click(); 
     } 

     static void AClicked(object sender, EventArgs e) 
     { 
      Console.WriteLine(sender.ToString()); 
     } 
    } 
} 
0

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

Мое решение заключалось в том, чтобы попасть в книги и стать более знакомыми с делегатами и обработчиками событий. Хотя я использовал оба в течение многих лет, я никогда не был знаком с ними. http://www.codeproject.com/Articles/20550/C-Event-Implementation-Fundamentals-Best-Practices дает лучшее объяснение как делегатов, так и обработчиков событий, которые я когда-либо читал, и ясно объясняет, что класс может быть издателем событий и использовать другие классы. В этой статье: http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single обсуждается, как однократные события обрабатываются только одним обработчиком, поскольку делегаты являются многоадресными по определению. Делегат наследует system.MulticastDelegate большинство, включая делегатов системы, являются многоадресными. Я обнаружил, что многоадресная рассылка означает, что любой обработчик событий с той же подписью получит поднятое событие. Многоадресное поведение вызвало у меня несколько бессонных ночей, когда я шагнул через код и увидел, что мое событие, казалось бы, ошибочно отправлено обработчикам, что я не собирался получать это событие. Обе статьи объясняют это поведение. Вторая статья показывает вам один путь, и первая статья показывает вам другую, сделав делегата и подпись жестко напечатанной. Я лично считаю, что сильная типизация предотвращает глупые ошибки, которые могут быть причиной боли. Поэтому я проголосовал за первую статью, хотя я получил второй код статьи. Мне было просто любопытно. :-)

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

Я начал играть со классом eventRaiser второй статьи. Я сделал простой проект в виде окон. Я добавил в свой проект второй класс статей EventRaiser.cs. В коде главной формы, я определил ссылку на этот EventRaiser класс на вершине, как

private EventRaiser eventRaiser = new EventRaiser(); 

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

protected void MainResponse(object sender, EventArgs eArgs) 
{    
    MessageBox.Show("got to MainResponse"); 
} 

затем в конструкторе главной формы я добавил назначение события:

eventRaiser.OnRaiseEvent += new EventHandler(MainResponse);` 

затем я создал класс, который должен быть создан с помощью моей главной формы, которая называется «Si mpleClass "из-за отсутствия творческой изобретательности на данный момент.

Затем я добавил кнопку, и в случае щелчка данной кнопки я инстанцирован код SimpleClass Я хотел поднять событие из:

private void button1_Click(object sender, EventArgs e) 
    {    
     SimpleClass sc = new SimpleClass(eventRaiser); 
    } 

Примечания экземпляра «eventRaiser», который я прошел в SimpleClass.cs , Это было определено и инстанцировано ранее в коде главной формы.

В SimpleClass:

using System.Windows.Forms; 
using SinglecastEvent; // see SingleCastEvent Project for info or http://www.codeproject.com/Articles/12285/Implementing-an-event-which-supports-only-a-single 

    namespace GenericTest 
    { 

     public class SimpleClass 
     { 

      private EventRaiser eventRaiser = new EventRaiser(); 

      public SimpleClass(EventRaiser ev) 
      { 
       eventRaiser = ev; 
       simpleMethod(); 

      } 
      private void simpleMethod() 
      { 

       MessageBox.Show("in FileWatcher.simple() about to raise the event"); 
       eventRaiser.RaiseEvent(); 
      } 
     } 
    } 

Единственный момент, к частному методу я назвал SimpleMethod было проверить, что в частном области видимости метод может еще поднять событие, не то, что я сомневалась, но мне нравится быть положительны.

Я запустил проект, и это привело к тому, что событие «SimpleMethod» из «SimpleClass» было перенесено на основную форму и перешло к ожидаемому правильному методу MainResponse, доказывая, что один класс действительно может поднять событие, которое потребляется другим классом. Да, событие должно быть поднято из класса, который нуждается в его трансляции для других классов, которым это необходимо. Прием классов может быть одним классом или многими многими классами в зависимости от того, насколько сильно вы набрали их, или сделав их одиночными, как в 2-й статье.

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

0

Подъемный класс должен получить свежую копию EventHandler. Возможное решение ниже.

using System; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     class HasEvent 
     { 
      public event EventHandler OnEnvent; 
      EventInvoker myInvoker; 

      public HasEvent() 
      { 
       myInvoker = new EventInvoker(this,() => OnEnvent); 
      } 

      public void MyInvokerRaising() { 
       myInvoker.Raise(); 
      } 

     } 

     class EventInvoker 
     { 
      private Func<EventHandler> GetEventHandler; 
      private object sender; 

      public EventInvoker(object sender, Func<EventHandler> GetEventHandler) 
      { 
       this.sender = sender; 
       this.GetEventHandler = GetEventHandler; 
      } 

      public void Raise() 
      { 
       if(null != GetEventHandler()) 
       { 
        GetEventHandler()(sender, new EventArgs()); 
       } 
      } 
     } 

     static void Main(string[] args) 
     { 
      HasEvent h = new HasEvent(); 
      h.OnEnvent += H_OnEnvent; 
      h.MyInvokerRaising(); 
     } 

     private static void H_OnEnvent(object sender, EventArgs e) 
     { 
      Console.WriteLine("FIRED"); 
     } 
    } 
} 
0

Не хорошее программирование, но если вы хотите сделать что-либо, как вы можете сделать что-то вроде этого

class Program 
{ 
    static void Main(string[] args) 
    { 

     Extension ext = new Extension(); 
     ext.MyEvent += ext_MyEvent; 
     ext.Dosomething(); 
    } 

    static void ext_MyEvent(int num) 
    { 
     Console.WriteLine(num); 
    } 
} 


public class Extension 
{ 
    public delegate void MyEventHandler(int num); 
    public event MyEventHandler MyEvent; 

    public void Dosomething() 
    { 
     int no = 0; 
     while(true){ 
      if(MyEvent!=null){ 
       MyEvent(++no); 
      } 
     } 
    } 
} 
Смежные вопросы