Why am I not allowed to do this with events
-
This is a genuine "why" question (as in the technical reason why the language prohibits what I want to do) rather than a "how do I get round the limitation?"* I'm writing a WPF app and implementing a base class for a
Command
. The base implementation contains common logic to a set of subclasses which must have the Database connection in a non-busy state before they can execute. If the busy state changes, calling CanExecuteChanged?.Invoke(this, e); is naturally fine in this base. The problem is I have secondary conditions in the subclasses which also prevent execution - on those conditiond changing I'd want to call CanExecuteChanged?.Invoke(this, e); from the subclass - this isn't allowed and I can't work out the reasoning. My current working theory is the even is like syntactic sugar over a delegates (or something) and the part to raise the event is private (which would prevent raising outside the base class) the whereas the subscription side is public (so you can subscribe from an unrelated type).That doesn't explain why the "raise" part isn't protected, or somehow optionally private/protected. There is a gap in my knowledge somewhere. * I have a couple of solutions for this: like exposing the delegate with a backing field and calling that, or the one I'll probably adopt which is to expose a protected event to raise the event seems more explicit.KeithBarrow.net[^] - It might not be very good, but at least it is free!
-
This is a genuine "why" question (as in the technical reason why the language prohibits what I want to do) rather than a "how do I get round the limitation?"* I'm writing a WPF app and implementing a base class for a
Command
. The base implementation contains common logic to a set of subclasses which must have the Database connection in a non-busy state before they can execute. If the busy state changes, calling CanExecuteChanged?.Invoke(this, e); is naturally fine in this base. The problem is I have secondary conditions in the subclasses which also prevent execution - on those conditiond changing I'd want to call CanExecuteChanged?.Invoke(this, e); from the subclass - this isn't allowed and I can't work out the reasoning. My current working theory is the even is like syntactic sugar over a delegates (or something) and the part to raise the event is private (which would prevent raising outside the base class) the whereas the subscription side is public (so you can subscribe from an unrelated type).That doesn't explain why the "raise" part isn't protected, or somehow optionally private/protected. There is a gap in my knowledge somewhere. * I have a couple of solutions for this: like exposing the delegate with a backing field and calling that, or the one I'll probably adopt which is to expose a protected event to raise the event seems more explicit.KeithBarrow.net[^] - It might not be very good, but at least it is free!
The technical reason is fairly simple. When you declare an event:
public class Foo
{
public event EventHandler Test;
}the compiler actually generates two members:
public class Foo
{
[CompilerGenerated]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private EventHandler Test;public event EventHandler Test { \[CompilerGenerated\] add { EventHandler eventHandler = this.Test; EventHandler eventHandler2; do { eventHandler2 = eventHandler; EventHandler value2 = (EventHandler)Delegate.Combine(eventHandler2, value); eventHandler = Interlocked.CompareExchange(ref this.Test, value2, eventHandler2); } while ((object)eventHandler != eventHandler2); } \[CompilerGenerated\] remove { EventHandler eventHandler = this.Test; EventHandler eventHandler2; do { eventHandler2 = eventHandler; EventHandler value2 = (EventHandler)Delegate.Remove(eventHandler2, value); eventHandler = Interlocked.CompareExchange(ref this.Test, value2, eventHandler2); } while ((object)eventHandler != eventHandler2); } }
}
Inside the same type, you will either reference the private field (for assignment, Invoke, etc.) or the event (for adding and removing handlers). The compiler will decide which. Outside of the class, you only have access to the event. All you can do is add or remove a handler. I believe this is to prevent other classes from raising events without the cooperation of the base class, or swapping out the entire list of handlers. But obviously you'd need to ask the original language designers to know their reasoning. Of course, the simple workaround is to provide a protected method in the base class which raises the event on demand.
protected void OnTest(EventArgs e)
{
Test?.Invoke(this, e);
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
-
The technical reason is fairly simple. When you declare an event:
public class Foo
{
public event EventHandler Test;
}the compiler actually generates two members:
public class Foo
{
[CompilerGenerated]
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
private EventHandler Test;public event EventHandler Test { \[CompilerGenerated\] add { EventHandler eventHandler = this.Test; EventHandler eventHandler2; do { eventHandler2 = eventHandler; EventHandler value2 = (EventHandler)Delegate.Combine(eventHandler2, value); eventHandler = Interlocked.CompareExchange(ref this.Test, value2, eventHandler2); } while ((object)eventHandler != eventHandler2); } \[CompilerGenerated\] remove { EventHandler eventHandler = this.Test; EventHandler eventHandler2; do { eventHandler2 = eventHandler; EventHandler value2 = (EventHandler)Delegate.Remove(eventHandler2, value); eventHandler = Interlocked.CompareExchange(ref this.Test, value2, eventHandler2); } while ((object)eventHandler != eventHandler2); } }
}
Inside the same type, you will either reference the private field (for assignment, Invoke, etc.) or the event (for adding and removing handlers). The compiler will decide which. Outside of the class, you only have access to the event. All you can do is add or remove a handler. I believe this is to prevent other classes from raising events without the cooperation of the base class, or swapping out the entire list of handlers. But obviously you'd need to ask the original language designers to know their reasoning. Of course, the simple workaround is to provide a protected method in the base class which raises the event on demand.
protected void OnTest(EventArgs e)
{
Test?.Invoke(this, e);
}
"These people looked deep within my soul and assigned me a number based on the order in which I joined." - Homer
Oh wow thanks, that's exactly what I was hoping for. It's also along the the "syntactic sugar" line of thinking. I've been off C# as my main work for 4/5years now so I'm getting to grips with it all again. I won't utter the name of the language I have been principally using, but it rhymes with Lavascript :~
KeithBarrow.net[^] - It might not be very good, but at least it is free!