关于c#自定义事件以及它们如何正常工作的快速问题 [英] Quick question about c# custom events and how they work exactly

查看:49
本文介绍了关于c#自定义事件以及它们如何正常工作的快速问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我只是想问一下我的想法是否正确,基本上我的课程名为'Person',然后在课堂上我有活动,

- OnCashChanged(Person x);

- OnCashAdded(Person x);

- OnCashRemoved(Person x); //意思是花钱:D



然后我想访问这些事件,这是我的问题,例如,如果你有多个人物,你需要倾听他们中的任何一个改变他们的现金或现金添加或删除,所以你可以从我的代码中看到,我有2个人,我宣布他们两个的所有事件,但这实际上是正确的吗?!因为如果你有50个人并且你需要在他们所有人身上听事件,你需要像我一样订阅每个人的事件,或者这是另一种方式来做这个,就像我说的那样了解事件,我不知道什么是正确的,什么不是,

链接到pastebin代码

提前感谢。

i just want to ask if my thinking is right, basically for example i have class named 'Person' and then in the class i have events,
- OnCashChanged(Person x);
- OnCashAdded(Person x);
- OnCashRemoved(Person x); // meaning spent money :D

and then i would like to access these events and here is my questions, for example if you have multiple objects of persons, and you need to listen on whatever one of them changes their cash or cash added or removed, so as you can see from my code, i have 2 persons and i declared the all events for the two of them, but is this actually correct?! because what if you have 50 persons and you need to listen for events on all of them, do you need to subscribe to the event with every one of them like i did or these is a some other way to do this, like i said im learning about events and i dont know what is correct and what not,
link to pastebin code
thanks in advance.

推荐答案

你编码的方式,是的,你需要注册每个人的活动。你可以做的是创建一个Person类型的集合类,这样它就集中在一个类上。像这样:



The way you have it coded, yes you would need to register to each person's event. What you could do is create a collection class of type Person, that way it's all centralized on one class. Like so:

class PersonCollection:List<person>{
     public new void Add(Person p){
          if(p!=null){
               // Register here to events
               p.OnCashChanged += OnCashChangedHandler;
               base.Add(p);
          }
     }

     public new bool Remove(Person p){
          bool removed=false;
          if(p!=null){
               // Remove events
               p.OnCashChanged -= OnCashChangedHandler;
               removed = base.Remove(p);
          }
          return removed;
     }
     
     protected void OnCashChangedHandler(Person p){
          // Do something here with someone
     }
}
</person>





这样,您的所有代码都集中到此类,然后您只需添加或删除集合中的人



This way, all your code is centralized to this class then you can just add or remove people from the collection


以下是使用Action委托类型进行事件通知的示例:
Here's an example of using the Action delegate Type for event notification:
using System;
using System.Collections.Generic;

namespace PersonCollectionDemo
{
    public enum TransactionType
    {
        ClearAndReset,
        Deposit,
        Withdraw
    }

    // Person
    public class Person
    {
        // expose only ability to 'read
        public string Name { private set; get; }
        public decimal Cash { private set; get; }

        public Person
        (
            string name, decimal initialCash
        )
        {
            Name = name;
            Cash = initialCash;
        }

        public void SetCash(decimal amount)
        {
            // consider throwing an error if amount is negative ?
            if (amount == Cash || amount < 0) return;

            Cash = amount;

            Persons.CashChangedMethod(this, TransactionType.ClearAndReset, amount, Cash);
        }

        public void AddCash(decimal amount)
        {
            // amount must be positive
            // consider throwing an error if negative ?
            if (amount <= 0) return;

            Cash += amount;

            Persons.CashChangedMethod(this, TransactionType.Deposit, amount, Cash);
        }

        public void WithDrawCash(decimal amount)
        {
            // amount must be negative
            // consider throwing an error if positive ?
            if (amount >= 0) return;

            // detect and handle overdraft
            if (Cash + amount < 0)
            {
                Persons.OverDraftAlertMethod(this, TransactionType.Withdraw, amount, Cash);
                return;
            }

            Cash += amount;

            Persons.CashChangedMethod(this, TransactionType.Withdraw, amount, Cash);        
        }
    }

    // Collection of Person
    public class Persons : List<Person>
    {
        public static Action<Person,> CashChangedMethod { private set; get; }

        public static Action<Person,> OverDraftAlertMethod { private set; get; }

        public Persons
        (
            Action<Person,> cashChangedMethod,
            Action<Person,> overDraftAlertMethod
        )
        {
            if(CashChangedMethod == null) CashChangedMethod = cashChangedMethod;
            if(OverDraftAlertMethod == null) OverDraftAlertMethod = overDraftAlertMethod;
        }

        public void Add(string name, decimal initialcash)
        {
            Person newPerson = new Person(name, initialcash);
            this.Add(newPerson);
        }
    }
}

示例测试:

using PersonCollectionDemo;

private void TestPersonDemo
{
    Persons ThePersons = new Persons(CashChangedMethod, OverDraftAlertMethod);
    
    ThePersons.Add("Person1", 100.23M);
    ThePersons[0].SetCash(245.35M);
    ThePersons.Add("Person2", 499.00M);
    
    ThePersons[0].AddCash(23.35M);
    ThePersons[1].WithDrawCash(-500.00M);
}

// EventHandlers invoked from Person class
private void OverDraftAlertMethod(Person person, TransactionType transactionType, decimal amount, decimal balance)
{
    Console.WriteLine("Account Action: Withdrawal Rejected: Account Name: {0} | Transaction Type: {1} | Withdraw Amount: {2} | Balance: {3}", person.Name, transactionType, amount, balance);
}

private void CashChangedMethod(Person person, TransactionType transactionType, decimal amount, decimal balance)
{
    Console.WriteLine("Account Action: Account Name: {0} | Transaction Type: {1} | Amount: {2} | Balance: {3}", person.Name, transactionType, amount, balance);
}

测试输出到控制台:

Account Action: Account Name: Person1 | Transaction Type: ClearAndReset | Amount: 245.35 | Balance: 245.35

Account Action: Account Name: Person1 | Transaction Type: Deposit | Amount: 23.35 | Balance: 268.70

Account Action: Withdrawal Rejected: Account Name: Person2 | Transaction Type: Withdraw | Withdraw Amount: -500.00 | Balance: 499.00

注意:



1.在此示例中需要注意使用Action Delegates的实例(声明它们作为'静态属性',只有第一次调用Persons类的构造函数时才可访问(可以设置)。



这样做的原因是为了防止类外的任何代码向这些Action Delegates添加额外的EventHandler。

Notes:

1. some care is taken in this example to make the instances of the Action Delegates used (they are declared as 'static Properties) only accessible (can be set) the first time the constructor of the Persons class is called.

The reason for this is to prevent any code outside the Class from adding additional EventHandlers to those Action Delegates.


开始与,这些不是事件。这不是事件和工作的方式,也不是事件数据的传递方式。请看我对这个问题的评论。



我会很快发送给你阅读有关事件的标准文档。无论如何,你需要学习这个主题。但是你还需要做一些在MSDN中没有正确解释的东西,因为这个东西并不是正式要求的,是文化代码的一些约定的一部分。也许最好向您展示一些代码示例。



首先,应使用System派生的类传递事件数据。 EventArgs的。这不是正式要求,但作为良好的风格惯例是必需的;作为回报,.NET通过其.NET BCL类型为您提供有价值的支持。这样做:

To start with, those are not events. And this is not how events and work and not how event data is passed. Please see my comments to the question.

I would quickly send you to the reading of standard documentation on events. You need to learn this topic anyway. But you also need to do something which is not properly explained in MSDN, because this "something" is not formally required, be is a part of some conventions for the cultured code. Perhaps it would be better to show you some code sample.

First of all, event data should be passed using the class derived from System.EventArgs. This is not required formally but is required as the good style convention; in return, .NET give you valuable support by its .NET BCL types. Do this:
class Person { /* ... */ }

public class MyEventArgs : System.EventArgs {
     internal MyEventArgs(Person person) { this.Person = person; } // not public!
     public Person Person { get; private set; } // private is important
}



现在,让我们添加声明一个事件实例的类。它可以是你的 Person ,但是当这是一个不同的类时,最好显示更一般的情况。首先,如果您使用常规委托实例而不是事件实例,则与事件委托类型匹配的委托类型可以基于 System.Action 声明。它可能是


Now, let's add the class declaring an event instance. It can be your Person, but it's better to show more general case when this is a different class. First of all, if you used a "regular" delegate instance, not event instance, your delegate type matching the event delegate type could be based on the System.Action declaration. It could be

System.Action<object, MyEventArgs> myDelegateInstance;

不过!此约定不适用于常规委托实例,而仅适用于委派实例。但对于委托实例,.NET BCL为您提供了更多不同的支持,因此您甚至不需要此 Action 声明。相反,你应该使用

But! This convention is not applied to "regular delegate instances", but only to delegate instances. But for delegate instances, .NET BCL gives you more , different support, so you don't even need this Action declaration. Instead, you should use

public class EventImplementor {
    public event System.EventHandler<MyEventArgs> PersonChanged;
}



现在,问题是:在哪里调用事件?诀窍是:它只允许在同一个类中完成;任何其他代码都不会编译。这是一个非常重要的.NET 防呆功能

因此,您需要添加:


Now, the problem is: where to invoke the event? The trick is: it is only allowed to be done in the same class; any other code won't compile. This is a very important .NET fool-proof feature.
So, you would need to add:

public class EventImplementor {
    
    public event System.EventHandler<MyEventArgs> PersonChanged;
    
    // private, by default, but could be, say, protected, to grant access
    // to derived classes:
    void InvokePersonChanged() {
        if (PersonChanged != null) // important!
            PersonChanged.Invoke(this, new MyEventArgs(this.person));
    }

    // now you can called it whenever something changes the person;
    // for example, it could be done by public/internal setter (!)
    // of the property:
    Person Person {
        get { return this.person; }
        set {
            if (value == this.person) return;
            this.person = value;
            InvokePersonChanged();
        }
    }
    
    Person person;

}



不是时候处理 OnSomethingChanged 了。惯例是:如果你有 Person 属性,名为 OnPersonChanged 1)的方法应该是虚拟的,2)只有受保护,3)应该调用 PersonChanged


Not it's time to address your OnSomethingChanged. The convention is: if you have the property Person, the method named like OnPersonChanged 1) should be virtual, 2) only protected, 3) should invoke PersonChanged:

public class EventImplementor {
    
    public event System.EventHandler<MyEventArgs> PersonChanged;
    
    // private, by default, but could be, say, protected, to grant access
    // to derived classes:
    protected virtual void OnPersonChanged() {
        if (PersonChanged != null) // important!
            PersonChanged.Invoke(this, new MyEventArgs(this.person));
    }

    // ...

}

为什么?由一个非常严重的原因。它为处理人的变化提供了一些额外的灵活性。第一种方式不会使用该事件,但不允许事件多播。它更快:

Why? By a very serious reason. It give some extra flexibility for handling the change of the person. First way won't use the event, but will not allow for event multi-casting. It's faster:

class MyDerivedClass : EventImplementor {
    protected override void OnPersonChanged() {
        // do something when person changed
        // note that you can ignore event, then it won't multi-cast
    } //protected override void OnPersonChanged()
}

另一种方法是处理事件;因为我公开了事件(可能是内部事件),所以可以在代码的任何地方完成,即使是在不同的程序集中:

Another way is handling the event; as I made event public (could be internal), it can be done in any place of code, even in a different assembly:

EventImplementor implementor = new EventImplementor();
implementor.PersonChanged += (sender, eventArgs) => {
    // this is also based on convention:
    // the declaring class member send "this" as sender:
    EventImplementor implementorSender = (EventImplementor)sender;
    // but for static event instances, it should be null
    Person person = eventArgs.Person; // this was the whole purpose
    // of the event argument class
};

你明白了吗?







我显然忽略了你改变一些哈希的事实,而不是人。首先,这是因为我想让我的教程尽可能简单。差别很小。您可以拥有两个:一个属性为person,另一个属性为哈希对象,而不是 MyEventArgs.Person 。此外,您应该添加更多事件,例如 PersonAdding PersonAdded PersonRemoving ,PersonRemoved。如果不清楚,请问我这个问题。







一些该网站的奇怪成员不接受匿名方法。我认为这是一个很大的错误,但为了完整性:

Are you getting the idea?



I apparently pretty much ignore the fact that you are changing some hash, not person. This if, first of all, because I wanted to make my tutorial as simple as possible. The difference is very little. Instead of having MyEventArgs.Person, you could have two: one property for person, another for hash object. Also, you should rather add more events, such as PersonAdding, PersonAdded, PersonRemoving, PersonRemoved. If this is not clear, please ask me the question.



Some weird members of this site don't accept anonymous methods. I think this is a big mistake, but for completeness:

static void PersonHandler(object sender, MyEventArgs eventArg) { /* ... */ }

//...

implementor.PersonChanged += PersonHandler; // no need in "new" 



对于C#.v2,即使对于匿名,也不能使用类型推断。你需要:


For C#.v2, type inference cannot be used even for the anonymous. You would need:

implementor.PersonChanged += delegate(object sender, MyEventArgs) => {
    // ...
}

可以将几个处理程序添加到每个事件处理程序的调用列表中;它们都将通过一次调用调用来调用。



-SA

Several handlers can be added to the invocation list of each event handler; they all will be invoked through one invocation call.

—SA


这篇关于关于c#自定义事件以及它们如何正常工作的快速问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆