繁体   English   中英

了解事件用法的委托和注册方法

[英]Understanding the event usage wrt delegates and registration methods

我试图了解代表和事件,到目前为止,我已经知道了概念。

我心中有一个问题,想知道我是否正确。

有一个班车。 我们创建一个公共委托(CarHandler),然后创建一个委托类型的私有成员(ListofMethods),然后在此方法中创建用于向该成员注册方法的方法(RegistorMethods),即ListofMethods + = incoming参数。

然后,在主程序中,我们创建签名与委托签名相同的方法(签名为返回类型void,参数为字符串)。 然后,我们创建Car类的对象。 然后我们向委托注册方法/方法(方法为console.writeline(incomming参数))。 然后,当我们调用此类时。 现在,基于在类中调用ListofMethods的位置(示例:ListofMethods(“ Hey There”);),将相应地触发RegistoredMethods。

因此,使用事件而不是上面的示例的优点是:我知道我们可以创建多个相同委托类型的事件,而无需创建更多的注册方法。

情况1仅使用委托,没有事件。 情况2使用事件。 然后,在情况1中,所有注册的方法将获得与ListofHandler调用的文本相同的文本。 要在案例1中创建更多事件(这里的事件表示通用英语含义,而不是c#事件),我们将需要创建更多的委托成员,并需要更多的方法向该委托成员注册新方法。 但是,在事件(案例2)的情况下,不同的事件可以给出自己的文本,然后实例可以注册所需的事件并将其触发。

在案例1中,我们将需要创建更多的委托成员来引发多个事件(不是C#事件,一般意义上的英语),而在案例2(事件)的情况下,仅创建1个委托成员就足够了。 那正确吗?

问题:以上最正确的方式来实现CASE 3,与情况2类似,但仅使用委托而不是事件。 请您在答案中写一个便条

如果不了解,您可以问我问题。 请在这里帮助我清除我的疑问。

案例1的代码:

public class Car
    {
        // 1) Define a delegate type. 
        public delegate void CarEngineHandler(string msgForCaller);

        // 2) Define a member variable of this delegate.   
        //this can be public, and if public then we can avoid writing the below RegisterWithCarEngine method, but it is not safe
        //because user can mess the values and call custom strings, etc
        private CarEngineHandler listOfHandlers;

        // 3) Add registration function for the caller.   
        public void RegisterWithCarEngine(CarEngineHandler methodToCall)
        {
            //listOfHandlers = methodToCall;  
            listOfHandlers += methodToCall;
        }

        // Internal state data.
        public int CurrentSpeed { get; set; }
        public int MaxSpeed { get; set; }
        public string PetName { get; set; }

        // Is the car alive or dead?  
        private bool carIsDead;

        // Class constructors.  
        public Car()
        {
            MaxSpeed = 100;
        }

        public Car(string name, int maxSp, int currSp)
        {
            CurrentSpeed = currSp;
            MaxSpeed = maxSp;
            PetName = name;
        }

        // 4) Implement the Accelerate() method to invoke the delegate's  
        //    invocation list under the correct circumstances. 
        public void Accelerate(int delta)
        {
            // If this car is "dead," send dead message.   
            if (carIsDead)
            {
                if (listOfHandlers != null)
                    listOfHandlers("Sorry, this car is dead...");
            }

            else
            {
                CurrentSpeed += delta;
                // Is this car "almost dead"? 
                if (10 == (MaxSpeed - CurrentSpeed) && listOfHandlers != null)
                {
                    listOfHandlers("Careful buddy! Gonna blow!");
                }
                if (CurrentSpeed >= MaxSpeed)
                    carIsDead = true;
                else
                    Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed);
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("***** Delegates as event enablers *****\n");
            // First, make a Car object.   
            Car c1 = new Car("SlugBug", 100, 10);
            // Now, tell the car which method to call     
            // when it wants to send us messages.     
            c1.RegisterWithCarEngine(new Car.CarEngineHandler(OnCarEngineEvent));
            // Speed up (this will trigger the events).     
            Console.WriteLine("***** Speeding up *****");
            for (int i = 0; i < 6; i++)
                c1.Accelerate(20);
            Console.ReadLine();

            Car c2 = new Car("SlugBug1", 100, 10);
            // Speed up (this will trigger the events).     
            Console.WriteLine("***** Speeding up *****");
            for (int i = 0; i < 6; i++)
                c2.Accelerate(20);
            Console.ReadLine();
        }
        // This is the target for incoming events. 
        public static void OnCarEngineEvent(string msg)
        {
            Console.WriteLine("\n***** Message From Car Object *****");
            Console.WriteLine("=> {0}", msg);
            Console.WriteLine("***********************************\n");
        }
    }

案例2的代码:

public class Car 
{   // This delegate works in conjunction with the 
  // Car's events.   
public delegate void CarEngineHandler(string msg); 
// This car can send these events.   
public event CarEngineHandler Exploded;   
public event CarEngineHandler AboutToBlow;  
...
 }

public void Accelerate(int delta) 
{   
// If the car is dead, fire Exploded event. 
  if (carIsDead) 
  {   
  if (Exploded != null) 
      Exploded("Sorry, this car is dead...");  
 } 
  else  
 {     CurrentSpeed += delta;  
    // Almost dead?
     if (10 == MaxSpeed - CurrentSpeed && AboutToBlow != null)  
   {      
 AboutToBlow("Careful buddy!  Gonna blow!");  
   }  
    // Still OK! 
    if (CurrentSpeed >= MaxSpeed)   
    carIsDead = true;
     else      
 Console.WriteLine("CurrentSpeed = {0}", CurrentSpeed); 
  } 
}

static void Main(string[] args)
 {   
Console.WriteLine("***** Fun with Events *****\n"); 
  Car c1 = new Car("SlugBug", 100, 10); 
// Register event handlers.  
 c1.AboutToBlow += CarIsAlmostDoomed; 
  c1.AboutToBlow += CarAboutToBlow; 
  c1.Exploded += CarExploded;  
  Console.WriteLine("***** Speeding up *****");  
 for (int i = 0; i < 6; i++)    
 c1.Accelerate(20);  
  c1.Exploded -= CarExploded;  
  Console.WriteLine("\n***** Speeding up *****"); 
  for (int i = 0; i < 6; i++)   
  c1.Accelerate(20);  
  Console.ReadLine(); 
public static void CarAboutToBlow(string msg)   { Console.WriteLine(msg); }  
  public static void CarIsAlmostDoomed(string msg)   { Console.WriteLine("=> Critical Message from Car: {0}", msg); }  
  public static void CarExploded(string msg)   { Console.WriteLine(msg); } 
} 

两种情况之间的区别基本上可以归结为这种区别:

情况1

public class Car
{
    void RegisterWithCarEngine(CarEngineHandler methodToCall);
}

情况二

public class Car
{
    event CarEngineHandler Exploded;   
    event CarEngineHandler AboutToBlow;
}

情况1很奇怪。 没有什么可以让此类的使用者知道此方法的作用-或何时触发。 而且,也许更重要的是,没有方法可以分离事件处理程序。

情况2更标准。 它符合给出良好命名约定的概念,并且很明显这两个成员是事件。 因此,对于消费者来说很明显,他们可以将这些事件附加和分离。

您需要考虑一下以下问题:

public class Car
{
    void SetSpeed(string speedName, int speed);
    int GetSpeed(string speedName);
}

然后,我可以这样编码:

car.SetSpeed("Max", 50);
car.SetSpeed("Current", 10);
Console.WriteLine(car.GetSpeed("Max"));
Console.WriteLine(car.GetSpeed("Current"));

现在,尽管这名义上提供了与您的类相同的功能-并且可能会争辩说它提供了更多的功能-但它隐藏了该类的使用者所看到的功能。

最好使用案例2提供的接口。

只是附带说明,您应该始终这样调用事件代码:

var x = Exploded;
if (x != null)
    x("Sorry, this car is dead...");

可以在null检查和调用之间删除Exploded上的委托。 临时分配可避免该问题。

您的两种情况几乎相同。 唯一的实质区别是,当您在类中使用event (即“情况2”)并且没有显式实现该事件时,编译器会自动生成您必须在“情况1”中声明的字段,以及允许订阅/注册的方法。

像我上面的这样的陈述经常让人感到惊讶,甚至偶尔使那些已经使用C#的人感到惊讶:

而且您没有明确实现它

那句话是什么意思? 许多人没有意识到,对于属性,可以让编译器实现成员,也可以自己实现。

对于该属性,可以实现get和/或set方法。 如果发生事件,这些方法分别称为addremove 当然,如果您自己实现,则还需要提供支持字段或其他机制来跟踪订户(就像在属性中一样)。


那么,这在您的特定示例中意味着什么? 好吧,对我来说,这意味着,如果您具有类似事件的语义,那么您绝对应该继续将其实现为实际的event成员。 不管您采用哪种方式,代码基本上都会全部编译为等效的IL,但是使用event可以利用语言的高级抽象。 这使代码更易于读取和编写,并因此更易于维护且更不可能包含错误。

您可以牢记“情况1”中的方法,以防万一您在声明event不起作用的情况下event工作(例如,与不处理或不支持的平台或API进行某种互操作)。 NET event范例)。 但是在大多数情况下, event才是路要走。


似乎您担心的一部分是delegate成员的问题(即声明的delegate类型)。 坦白说,无论您采用哪种方式解决问题,都遇到了这个问题。 如果您有一种方法可以为一个类中的多个event成员重用单个委托类型,那么您也可以将该单个委托类型用于显式的域和注册方法(“情况1”)。

在大多数情况下,无论如何您都不应该声明自己的委托类型。 只需使用EventHandler<T>或通用ActionFunc类型之一即可。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM