在C#中从不同的类中引发类的事件

我有一个类EventContainer.cs,它包含一个事件,比如说:

public event EventHandler AfterSearch; 

我有另一个类,EventRaiser.cs。 如何从这堂课中筹集(而不是处理)上述事件?

引发的事件将依次调用EventContainer类中的事件处理程序。 这样的事情(这显然不正确):

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

这是不可能的,事件只能从课堂内升起。 如果你能做到这一点,那就会破坏事件的目的(能够从class级内部提升状态变化)。 我认为你误解了事件的function – 一个事件在一个类中定义,而其他人可以通过这样做来订阅它

obj.AfterSearch += handler; (其中handler是根据AfterSearch签名的AfterSearch )。 一个人能够从外部订阅事件就好了,但它只能从定义它的类中升级。

这是可能的 ,但使用聪明的黑客。

灵感来自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()); } } } 

看起来你正在使用Delegate模式 。 在这种情况下,应在EventRaiser类上定义AfterSearch事件,并且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 } 

您可以在希望事件触发的类上编写公共方法,并在调用事件时触发事件。 然后,您可以从您class级的任何用户调用此方法。

当然,这个废墟封装和糟糕的设计。

有很好的方法可以做到这一点。 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(..., ...); ... } 

同意Femaref – 并注意这是代表和事件之间的重要区别(例如,参见此博客条目,以便对此和其他差异进行良好讨论)。

根据您想要实现的目标,您可能会更好地与代表联系。

不是一个好的编程,但如果你想以任何方式这样做,你可以做这样的事情

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

我偶然发现了这个问题,因为我正在尝试从外部调用PropertyChanged事件。 所以你不必在每个class级都实现一切。 来自halorty的解决方案无法使用接口。

我找到了使用重reflection的解决方案。 它肯定很慢,并且打破了只能从类内部调用事件的原则。 但是找到这个问题的通用解决方案很有意思….

它的工作原理是因为每个事件都是一个被调用的调用方法列表。 因此,我们可以获取调用列表并通过我们自己的方式调用附加到该事件的每个侦听器。

干得好….

 class Program { static void Main(string[] args) { var instance = new TestPropertyChanged(); instance.PropertyChanged += PropertyChanged; instance.RaiseEvent(nameof(INotifyPropertyChanged.PropertyChanged), new PropertyChangedEventArgs("Hi There from anywhere")); Console.ReadLine(); } private static void PropertyChanged(object sender, PropertyChangedEventArgs e) { Console.WriteLine(e.PropertyName); } } public static class PropertyRaiser { private static readonly BindingFlags staticFlags = BindingFlags.Instance | BindingFlags.NonPublic; public static void RaiseEvent(this object instance, string eventName, EventArgs e) { var type = instance.GetType(); var eventField = type.GetField(eventName, staticFlags); if (eventField == null) throw new Exception($"Event with name {eventName} could not be found."); var multicastDelegate = eventField.GetValue(instance) as MulticastDelegate; if (multicastDelegate == null) return; var invocationList = multicastDelegate.GetInvocationList(); foreach (var invocationMethod in invocationList) invocationMethod.DynamicInvoke(new[] {instance, e}); } } public class TestPropertyChanged : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; } 

我有类似的困惑,老老实实地发现这里的答案令人困惑。 虽然一对夫妇暗示我后来发现可以解决的解决方案。

我的解决方案是阅读书籍,熟悉代表和事件处理程序。 虽然我已经使用了很多年,但我从未对它们非常熟悉。 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讨论了如何将事件单播到一个处理程序,因为委托是按定义进行多播的。 委托inheritancesystem.MulticastDelegate,其中大部分包括系统委托都是Multicast。 我发现多播意味着任何具有相同签名的事件处理程序都会收到引发的事件。 多播行为给我带来了一些不眠之夜,因为我通过代码看到我的事件似乎被错误地发送给处理程序,我无意得到这个事件。 两篇文章都解释了这种行为 第二篇文章向您展示了一种方式,第一篇文章通过使委托和签名紧密输入,向您展示了另一种方式。 我个人认为强大的打字可以防止可能很难找到的愚蠢错误。 所以我会投票给第一篇文章,尽管我得到了第二篇文章代码。 我只是好奇而已。 🙂

我也很好奇,如果我能得到#2文章代码,就像我解释上面的原始问题一样。 无论你选择何种方法,或者我是否也误解了原始问题,我的真实信息是我仍然认为你会像我一样阅读第一篇文章而受益,特别是如果这个页面上的问题或答案让你感到困惑。 如果您正在进行多播噩梦并需要快速解决方案,那么第2条可能会对您有所帮助。

我开始玩第二篇文章的eventRaiser类。 我做了一个简单的Windows窗体项目。 我将第二篇文章类EventRaiser.cs添加到我的项目中。 在Main表单的代码中,我在顶部定义了对EventRaiser类的引用

 private EventRaiser eventRaiser = new EventRaiser(); 

我在主要表单代码中添加了一个方法,我希望在事件被触发时调用该方法

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

然后在主窗体的构造函数中添加了事件赋值:

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

然后我创建了一个类,由我的主要表单“SimpleClass”实例化,因为目前缺乏创造性的聪明才智。

然后我添加了一个按钮,在按钮的click事件中,我实例化了我想要从以下方面引发事件的SimpleClass代码:

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

请注意我传递给SimpleClass.cs的“eventRaiser”实例。 这是在Main表单代码中更早定义和实例化的。

在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的私有方法的唯一要点是validation私有范围的方法仍然可以引发事件,而不是我怀疑它,但我喜欢积极。

我运行了项目,这导致事件从“SimpleClass”的“simpleMethod”升级到主窗体,并转到名为MainResponse的预期正确方法,certificate一个类确实可以引发一个由不同的类消耗的事件类。 是的,必须从需要将其更改广播到其他关注类的课程中提升。 接收类可以是一个类或许多类,具体取决于您定义它们的强度类型,或者如第二篇文章中那样使它们成为单个类。

希望这有助于而不是浑水。 就个人而言,我有很多代表和活动需要清理! 多播恶魔开始了!

提升class必须获得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 GetEventHandler; private object sender; public EventInvoker(object sender, Func 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"); } } } 

很简单的例子。 我喜欢用EventHandler这样做。

  class Program { static void Main(string[] args) { MyExtension ext = new MyExtension(); ext.MyEvent += ext_MyEvent; ext.Dosomething(); Console.ReadLine(); } static void ext_MyEvent(object sender, int num) { Console.WriteLine("Event fired.... "+num); } } public class MyExtension { public event EventHandler MyEvent; public void Dosomething() { int no = 1; if (MyEvent != null) MyEvent(this, ++no); } } }