对待事件作为对象对象、事件

2023-09-03 15:35:26 作者:中华一样的高傲

C#是还没有OO就够了吗?在这里,我给一个(也许坏)的例子。

 公共类节目
{
   公共事件的EventHandler的OnStart;
   公共静态事件处理程序LogOnStart =(S,E)=> Console.WriteLine(开始);

   公共类MyCSharpProgram
   {
      公共字符串名称{;组; }
      公共事件的EventHandler的OnStart;
      公共无效启动()
      {
          的OnStart(这一点,EventArgs.Empty);
      }
   }

   静态无效的主要(字串[] args)
   {
      MyCSharpProgram CS =新MyCSharpProgram {名称=C#测试};
      cs.OnStart + = LogOnStart; //可以编译
      //RegisterLogger(cs.OnStart); //麻烦线
      cs.Start(); //它打印开始(当然,这将:D)

      程序P =新计划();
      RegisterLogger(p.OnStart); //可以编译
      p.OnStart(对,EventArgs.Empty); //可以编译,但NullReference在运行时
      Console.Read();
    }

    静态无效RegisterLogger(事件处理程序EV)
    {
      EV + = LogOnStart;
    }
}
 

RegisterLogger(cs.OnStart)导致编译错误,因为事件XXX只能出现在+ =或左手侧 - =布拉布拉。但是,为什么RegisterLogger(p.OnStart)可以吗?同时,虽然我注册p.OnStart,这也将引发的NullReferenceException,似乎p.OnStart是不是真的传递给方法。

解决方案   

XXX只能出现在左手侧上的事件+ =或 - =布拉布拉

这其实是因为C#的是OO就好了。一个面向对象编程的核心原则就是封装;事件提供了这一种形式,就像属性:声明类里面,他们可以直接访问,但是外他们只接触到了 + = - = 运营商。这是这样,声明类是在事件被调用时的完全控制。客户端code只能有发言权的会发生什么的时候,他们被称为。

的原因,你的code RegisterLogger(p.OnStart)编译的是,它是从程序的范围类,其中 Program.OnStart 事件声明。

的原因,你的code RegisterLogger(cs.OnStart)做的不可以编译的是,它是从的范围内声明计划类,但 MyCSharpProgram.OnStart 声明事件(显然)在 MyCSharpProgram 类。

由于克里斯·泰勒指出,之所以你会得到一个的NullReferenceException 就行了 p.OnStart(P,EventArgs.Empty); 是调用 RegisterLogger 因为你拥有它的赋新值给本地变量,不必其中,当它传递作为一个参数的局部变量被分配的对象没有影响。为了更好的理解,请看下面code:

 静态无效IncrementValue(int值)
{
    值+ = 1;
}

INT I = 0;
IncrementValue(ⅰ);

//输出'0',因为IncrementValue对我没有任何影响 - 
//一个新值被分配到我这是通过在COPY
Console.WriteLine(ⅰ);
 
兵哥哥把你当作结婚对象,才会对你做的那些事

正如一个方法,它接受一个 INT 作为一个参数,并分配一个新的值给它仅影响复制到其堆栈中的局部变量,一个方法,需要一个< $ C C>事件处理程序 $作为一个参数,并分配一个新值的是的只影响的及其的局部变量,以及(在分配)。

C# is still not OO enough? Here I'm giving a (maybe bad) example.

public class Program
{
   public event EventHandler OnStart;
   public static EventHandler LogOnStart = (s, e) => Console.WriteLine("starts");

   public class MyCSharpProgram
   {
      public string Name { get; set; }
      public event EventHandler OnStart;
      public void Start()
      {
          OnStart(this, EventArgs.Empty);
      }
   }

   static void Main(string[] args)
   {
      MyCSharpProgram cs = new MyCSharpProgram { Name = "C# test" };
      cs.OnStart += LogOnStart;  //can compile
      //RegisterLogger(cs.OnStart);   // Line of trouble
      cs.Start();   // it prints "start" (of course it will :D)

      Program p = new Program();
      RegisterLogger(p.OnStart);   //can compile
      p.OnStart(p, EventArgs.Empty); //can compile, but NullReference at runtime
      Console.Read();
    }

    static void RegisterLogger(EventHandler ev)
    {
      ev += LogOnStart;
    }
}

RegisterLogger(cs.OnStart) leads to compile error, because "The event XXX can only appear on the left hand side of += or -= blabla". But why RegisterLogger(p.OnStart) can? Meanwhile, although I registered p.OnStart, it will also throw an NullReferenceException, seems that p.OnStart is not "really" passed to a method.

解决方案

"The event XXX can only appear on the left hand side of += or -= blabla"

This is actually because C# is "OO enough." One of the core principles of OOP is encapsulation; events provide a form of this, just like properties: inside the declaring class they may be accessed directly, but outside they are only exposed to the += and -= operators. This is so that the declaring class is in complete control of when the events are called. Client code can only have a say in what happens when they are called.

The reason your code RegisterLogger(p.OnStart) compiles is that it is declared from within the scope of the Program class, where the Program.OnStart event is declared.

The reason your code RegisterLogger(cs.OnStart) does not compile is that it is declared from within the scope of the Program class, but the MyCSharpProgram.OnStart event is declared (obviously) within the MyCSharpProgram class.

As Chris Taylor points out, the reason you get a NullReferenceException on the line p.OnStart(p, EventArgs.Empty); is that calling RegisterLogger as you have it assigns a new value to a local variable, having no affect on the object to which that local variable was assigned when it was passed in as a parameter. To understand this better, consider the following code:

static void IncrementValue(int value)
{
    value += 1;
}

int i = 0;
IncrementValue(i);

// Prints '0', because IncrementValue had no effect on i --
// a new value was assigned to the COPY of i that was passed in
Console.WriteLine(i);

Just as a method that takes an int as a parameter and assigns a new value to it only affects the local variable copied to its stack, a method that takes an EventHandler as a parameter and assigns a new value to it only affects its local variable as well (in an assignment).