理解事件用法wrt委托和注册方法

时间:2015-05-06 06:49:40

标签: c# events delegates

我正在努力了解代表和事件,到目前为止我知道这些概念。

我有一个问题,想知道我是不对。

有一类汽车。我们创建一个公共委托(CarHandler),然后我们创建委托类型的私有成员(ListofMethods),然后我们创建用这个成员注册方法的方法(RegistorMethods),我们说ListofMethods + =传入参数。

然后,在主程序中,我们创建具有与委托相同签名的方法(签名是返回类型void,参数是字符串)。然后,我们创建Car类的对象。然后我们用delegate注册方法/方法(方法是console.writeline(incomming parameter))。然后当我们调用这个类时。现在,根据类中调用ListofMethods的位置(例如:ListofMethods(“Hey There”);),相应地注册RegistoredMethods。

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

案例1仅使用委托而不使用任何事件。案例2正在使用事件。然后,在案例1中,所有已注册的方法将获得与ListofHandler调用的相同的文本。要在案例1中创建更多事件(这里的事件意味着一般英语含义而不是c#事件),我们需要创建更多委托成员,使用此委托成员注册新方法的更多方法。但是,在事件的情况下(案例2),不同的事件可以提供自己的文本,然后实例可以注册它所需的事件,并且它会被解雇。

在CASE 1中,我们需要创建更多的委托成员来提升多个事件(而不是C#事件,一般英语意思),在CASE 2(事件)的情况下,仅创建1个委托成员就足够了。是吗?

问题: 以上是正确的方法来实现CASE 3,就像案例2,但只使用委托而不是事件。请你在答案中写下这方面的说明

如果不理解那么你可以问我问题。请帮我在这里表达我的怀疑。

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

2 个答案:

答案 0 :(得分:1)

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

案例1

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

案例2

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...");

可以在Exploded支票和通话之间删除null上的代理人。临时分配可以防止该问题。

答案 1 :(得分:1)

你的两个案件几乎相同。唯一的不同之处在于,当您在类中使用event(即“案例2”)并且未明确实现它时,编译器会自动生成您必须在“案例1“,以及允许订阅/注册的方法。

经常让人惊喜的事情,即使偶尔会使用C#一段时间的人,也是如上所述的声明:

  

并且您没有明确地实现它

这句话是什么意思?许多人没有意识到,就像使用属性一样,可以让编译器实现成员,也可以自己完成。

如果是属性,则实施get和/或set方法。对于事件,方法名为addremove。当然,如果您自己实现它,您还需要提供支持字段或其他机制来跟踪订阅者(就像在属性中一样)。


那么,这在你的具体例子中意味着什么呢?好吧,对我来说,这意味着如果你有类似事件的语义,那么你肯定应该继续将其作为一个真正的event成员来实现。无论你采用哪种方式,代码都将基本编译为等效的IL,但使用event可以利用语言的高级抽象。这使得代码更易于读写,因此使其更易于维护,并且不太可能包含错误。

您可以记住“案例1”中的方法,以防您在声明event不起作用的情况下结束(例如某种与平台或API无法互操作的情况)处理或支持.NET event范例。但在大多数情况下,event是可行的方法。


您关注的部分似乎是delegate成员的问题(即声明的delegate类型)。坦率地说,无论您采用哪种方式处理问题,都会遇到此问题。如果您有一种方法可以为类中的多个event成员重用单个委托类型,那么您还可以将该单个委托类型重用于显式字段和注册方法方法(“案例1”)。

在大多数情况下,您不应该声明自己的委托类型。只需使用例如EventHandler<T>,或其中一种通用ActionFunc类型。

相关问题