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

sam*_*mar 28 c# events

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

public event EventHandler AfterSearch;
Run Code Online (Sandbox Code Playgroud)

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

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

EventContainer obj = new EventContainer(); 
RaiseEvent(obj.AfterSearch);
Run Code Online (Sandbox Code Playgroud)

Fem*_*ref 37

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

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

  • @Femaref:这是**可能**,请尝试下面的答案. (4认同)
  • @Femaref:我没有说它是实用性的,我说的是“这是可能的”,因为你说“这是不可能的,事件只能从班级内部引发”——但事实并非如此。 (3认同)
  • 有没有实用性?不,它没有,你的第一行说它是一个黑客,如果我在源代码中的某个地方发现它,我会慢慢关闭窗口并冷静地工作,再也不会出现。 (2认同)

hal*_*rty 25

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

灵感来自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());
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这并不能回答问题。您不是从类外部引发事件,而是调用一个从类内部引发事件的函数,这就是上面的答案所说的。如果您需要的只是通知另一个类,则应该使用中介者模式(该解决方案大致基于该模式)。 (2认同)

Mar*_*Pim 9

看起来你正在使用Delegate模式.在这种情况下,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();
}
Run Code Online (Sandbox Code Playgroud)

在EventContainer.cs中

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

    er.AfterSearch += this.OnAfterSearch;
}

public void OnAfterSearch()
{
   // Handle AfterSearch event
}
Run Code Online (Sandbox Code Playgroud)


Ode*_*ded 7

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

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

  • 我绝对同意你的观点,这将是一个糟糕的设计,但我经常使用的模式是有一个名为“GlobalEvents”的“公共静态类”,你可以调用它来传递_any_类可以监听的全局事件。当前执行此操作的唯一方法是使用“System.Action”。当我想要命名参数时,问题就出现了。目前有一个 XML 摘要标签,您可以用参数填充该标签,但它仍然不太可读,并且大多数 IDE 的自动摘要功能本身不支持它。 (2认同)

fea*_*eal 5

我也偶然发现了这个问题,因为我正在尝试从外部调用 PropertyChanged 事件。因此,您不必在每个类中都实现所有内容。halorty 的解决方案不能使用接口工作。

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

它起作用是因为每个事件都是被调用的调用方法列表。因此,我们可以获取调用列表并自行调用附​​加到该事件的每个侦听器。

干得好....

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;
}
Run Code Online (Sandbox Code Playgroud)