事件驱动的编程

我一直在阅读这篇MSDN文章和这个问题,试图理解.NET中的事件。 不幸的是,它没有点击我,我有很多麻烦。 我试图将这种技术整合到我的项目中,但很少成功。

基本上,我有这个班将读取数字。 每当遇到新号码时,我都希望它发起一个名为numberChanged的事件。

所以,我设置了我的事件public event EventHandler numberChanged; 。 稍后,当我遇到一个数字时,我会触发我的事件,而不是与之前的事件不同。

if(currentNumber != previousNumber){
     if(numberChanged != null){
          numberChanged(this, new EventArgs());
     }
}

但是,我在这个事件中遇到了麻烦。 如果我做numberChanged += [something to do here]它的错误说numberChanged是一个事件,而不是一个类型。

我的解释是否足够清晰,以提供一些建议? 非常感谢。


有很多方法来处理它,最基本的是创建一个函数:

public void MyNumberChangedHandler(object sender, EventArgs e)
{
    //Your code goes here that gets called when the number changes
}

然后您通过以下方式订阅(仅限一次,通常在构造函数中):

numberChanged += MyNumberChangedHandler;

或者,您可以使用一种称为匿名(lambda)方法的方法,该方法也在您的构造函数中(通常情况下)分配:

numberChanged += (sender, e) => {
    //Your code here to handle the number changed event
};

要扩大一点,使用lambda方法时必须小心,因为你可以创建内存泄漏和僵尸对象。 .NET内存垃圾收集器是一种标记扫描系统,可以在对象不再使用时删除对象。 这篇文章显示了如何去除lambda事件处理程序:如何去除lambda事件处理程序。

拥有一个活动的事件处理程序可以让对象保持活动状态, 即使它已被处置! 这是一个创建僵尸对象的例子(不在Fiddle中运行,但可以复制到自己的控制台应用程序)https://dotnetfiddle.net/EfNpZ5

打印出来:

I'm still alive
I'm still alive
I was disposed!
Press any key to quit
I'm still alive
I'm still alive
I'm still alive.

与C#编程世界中的其他所有事件一样,事件概念也遵循特定规则并具有自己的语法。 措辞如下:

  • 定义为EventHandler的事件实际上只是特殊方法(委托) 签名的快捷方式 - public delegate void EventHandler(object sender, EventArgs e) [1]。 每当你在C#中签名时,你总是知道你需要在正确的视点上写下什么或者作为参数,以便连接/调用某些对象/方法/等等。
  • 事件定义后,您需要订阅才能在发生事件时得到通知。 订阅事件的语法是+ = 。 当然,取消订阅是- = 。 MSDN说,语法应该是object.event += eventHandler (或object.event += new EventHandler(eventHandler);
  • 所以在定义一个事件( event Event SomeEvent; )之后,剩下的就是创建一个可以绑定到该事件的方法。 这个方法必须和 EventHandler 具有相同的签名,所以它应该匹配[1]的签名,并且可以像private void numberChangedEventHandler(object sender, EventArgs eventArguments)
  • 现在你知道你需要写在+ =的右边。

    一个例子:

    public class NumberSequence
    {
        // numbers to be compared
        private readonly List<int> numbers = new List<int>();
        // used to generate a random collection
        private readonly Random random = new Random();
        // tell me if the previous and next number are different
        public event EventHandler DifferentNumbersEvent;
    
        public NumberSequence()
        {
            // fill the list with random numbers
            Enumerable.Range(1, 100).ToList().ForEach(number =>
            {
                numbers.Add(random.Next(1, 100));
            });
        }
    
        public List<int> Numbers { get { return numbers; } }
    
        public void TraverseList()
        {
            for (var i = 1; i < this.numbers.Count; i++)
            {
                if (this.numbers[i - 1] != this.numbers[i])
                {
                    if (this.DifferentNumbersEvent != null)
                    {
                        // whoever listens - inform him
                        this.DifferentNumbersEvent(this, EventArgs.Empty);
                    }
                }
            }
        }
    }
    

    现在在使用类之前,定义事件处理程序,它将侦听并且将在事件被触发时再次调用:

    private void differentNumberEventHandler(Object sender, EventArgs eventArguments)
    {
        Console.WriteLine("Different numbers...");
    }
    

    用法:

    var ns = new NumberSequence();
    ns.DifferentNumbersEvent += differentNumberEventHandler;
    ns.TraverseList();
    

    例如:其他所有内容都只是语法糖(lambda / anonymous methods / ...)。

    object.Event += (s, e) => { // code ... };object.Event += (Object sender, EventArgs eventArguments) => { // code ... };相同object.Event += (Object sender, EventArgs eventArguments) => { // code ... }; 。 你认出签名吗? - 它与private void differentNumberEventHandler...

    通常我们需要通过事件传递信息,在这种情况下,我们可能希望看到这两个数字。 C#允许您使用自定义事件参数轻松完成此操作。 只需创建一个继承EventArgs类的类并为应该传递的数据添加属性,在这种情况下为数字:

    public class NumbersInfoEventArgs : EventArgs
    {
        public int Number1 { get; set; }
        public int Number2 { get; set; }
    }
    

    然后在声明事件时指定它将传递NumbersInfoEventArgs类型的数据(再次签名):

    public event EventHandler<NumbersInfoEventArgs> DifferentNumbersEvent;
    ...
    this.DifferentNumbersEvent(this, new NumbersInfoEventArgs
    {
        Number1 = this.numbers[i - 1],
        Number2 = this.numbers[i]
    });
    

    最后但现在至少,事件处理程序的签名应该匹配事件的签名:

    private void differentNumberEventHandler(Object sender, NumbersInfoEventArgs eventArguments)
    {
        Console.WriteLine("Different numbers {0} - {1}", eventArguments.Number1, eventArguments.Number2);
    }
    

    瞧,输出结果是:

    Different numbers 89 - 86
    Different numbers 86 - 53
    Different numbers 53 - 12
    Different numbers 12 - 69
    

    您可以通过以下方式订阅活动:

    using System;
    
    public class Program
    {
        public static void Main()
        {
            Console.WriteLine("Hello World");
            var num = new Number();
            num.numberChanged +=(s,e) =>{
                Console.WriteLine("Value was changed to {0}",num.Value); // in the demo below you can find another implementation for this sample using custom events
            };
            num.Value=10;
            num.Value=100;
        }
    }
    
    public class Number{
        public event EventHandler numberChanged;
        private int _value=0;
        public int Value
        {
            get{
                return _value;
            }
            set{
                if(value!=_value){
                    _value=value;
                    if(numberChanged!=null)
                        numberChanged(this,null);
                }
            }
        }
    }
    

    说明:

    因为EventHandler委托具有2个参数(sender,eventArgs),所以需要传递这些参数,并将它们传递为se

    另一种方式来订阅这样的事件:

    var num = new Number();
    num.numberChanged += NumberChanged_Event; // below is the delegate method
    
    public void NumberChanged_Event(object sender,EventArgs e)
    {
       // your code goes here
    }
    

    我更新了演示以与您自己的代理合作,传递在许多情况下可以提供帮助的旧值和新值。

    这里是一个工作演示

    链接地址: http://www.djcxy.com/p/51443.html

    上一篇: Event Driven Programming

    下一篇: Best practices of using lambda expressions for event handlers