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.
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 theProgramclass, where theProgram.OnStartevent is declared.The reason your code
RegisterLogger(cs.OnStart)does not compile is that it is declared from within the scope of theProgramclass, but theMyCSharpProgram.OnStartevent is declared (obviously) within theMyCSharpProgramclass.As Chris Taylor points out, the reason you get a
NullReferenceExceptionon the linep.OnStart(p, EventArgs.Empty);is that callingRegisterLoggeras 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:Just as a method that takes an
intas a parameter and assigns a new value to it only affects the local variable copied to its stack, a method that takes anEventHandleras a parameter and assigns a new value to it only affects its local variable as well (in an assignment).