I have a data structure class that is a child of a larger data/state class.
The inner data structure fires an event when the contained data changes. This event is consumed by the larger data/state class. The data/state class then fires its own event so that it may pass additional information along to the next event handler.
Public class Data
{
//properties and objects go here
public int Count
{
get { return _count; }
internal set
{
//if the count grew simply set _count
if (value != _oldCount)
{
_oldCount = _count;
_count = value;
}
//if the count shrank then set the count and trigger an event if the count is under 100
else
{
_oldCount = _count;
_count = value;
if (_count < 100)
{
CountChanged(this, new EventArgs());
}
}
}
}
public event EventHandler CountChanged;
}
The above event is consumed by this event handler
Data.CountChanged += new EventHandler(DataCountChanged);
private void DataCountChanged(object sender, EventArgs e)
{
DataRemoved(this, e); //Handle the old event and trigger a new event to pass along more information
}
public event EventHandler DataRemoved;
Finally the second event should be handled by another event handler to do some work. Unfortunately the call to trigger the second event fails with a NullReferenceException more often than not. Why?
----EDIT----
I understand that checking for Null will prevent the exception. The confusion is why this event is Null in the first place =D
You should always raise events using the following pattern to avoid null references and threading issues:
private void DataCountChanged(object sender, EventArgs e)
{
var dr = DataRemoved;
if (dr != null)
{
dr(this, e);
}
}
The reason the handler is null is that it should be viewed as a special collection of delegates. When the collection is empty the delegate has a null value. When you attach one or more handlers the collection is no longer empty and thus is no longer null.
if(DataRemoved != null && DataRemoved.GetInvocationList().Length > 0)
{
}
Assigning an empty delegate to your events may not be such a good design practice. Events are essentially delegates which are like function pointers. In other words, they are just like other reference members in your class. Unless you assign them a value or subscribe to them, they will be and should be null.
The null reference exception you get is for the same reason as declaring private MyClass; and then trying to use it before it has been assigned a value.
When you subscribe to an event, you are essentially telling the event which function to call. If your event does not have at least one such function pointer, it would not be in existence (NULL).
There is a trick to avoid the null check:
Just initialize your event as follows:
public event YourDelegate MyEvent = delegate { };
This way you do not need to check for nulls just call the event as usual:
this.MyEvent("Hi there!");
Edited
To clarify:
Declaring an event like this:
public event Action MyEvent;
It's translated automatically to:
private Action myEvent;
public event Action MyEvent
{
add
{
this.myEvent += value;
}
remove
{
this.myEvent -= value;
}
}
Therefore initializing an event like this:
public event Action MyEvent = delegate { };
It's safe because external code can not assign a null to the event itself.
You can however, assign null to the event inside the class it was declares but what really is happening is that, you are assigning null to the private delegate used by the event.
Source: Jon Skeet, C# In Depth, Events
Related
I am new to c# and unity, and want to use EventHandler to announce other script for doing something.
Some code have been subscribed to this event.
What "if(RefreshLevel != null)" is actually do, what is the content of "RefreshLevel", and why this event is not triggered?
using System;
using UnityEngine;
public class GameLevel : MonoBehaviour
{
public static GameLevel current;
private void Awake()
{
current = this;
}
private int level = 1;
private int manyItem;
private int burnedItem = 0;
public event EventHandler<LevelEventArgs> RefreshLevel;
// Start is called before the first frame update
void Start()
{
itemWorld = GameObject.Find("ItemWorld");
manyItem = itemWorld.transform.childCount;
}
public void LevelUp()
{
burnedItem += 1;
if (burnedItem < manyItem)
{
level += 1;
if(RefreshLevel != null)
{
RefreshLevel(this, new LevelEventArgs(level));
}
Debug.Log("Burned Item: " + burnedItem);
Debug.Log("Level: " + level);
}
else if(burnedItem == manyItem)
{
Debug.Log("Burned Item: " + burnedItem);
Debug.Log("Ghost Dead");
}
}
}
public class LevelEventArgs : EventArgs
{
public LevelEventArgs(int level)
{
Level = level;
}
public int Level;
}
TL;DR
if(RefreshLevel != null)
does nothing else than checking if there is anyone "listening" to that event. As long as nobody attached a listener/callback to the event (the invocation list is empty) it is equal to null and invoking it would throw a NullReferenceException.
You can also write it as
RefreshLevel?.Invoke(this, new LevelEventArgs(level));
which is a) shorter to write and b) makes clearer that this is an event and not a normal method.
Background
EventHandler<T>
public delegate void EventHandler(object? sender, EventArgs e);
is just a delegate, meaning a template for a method signature (similar to an interface for classes)
Then the script is using it as an event which has a special meaning
Events are a special kind of multicast delegate that can only be invoked from within the class or struct where they are declared (the publisher class). If other classes or structs subscribe to the event, their event handler methods will be called when the publisher class raises the event. For more information and code examples, see Events and Delegates.
And behind it there is a MulticastDelegate
Represents a multicast delegate; that is, a delegate that can have more than one element in its invocation list.
That "invocation list" are the registered callbacks.
And finally there is the operator MulticastDelegate.Inequality which returns
true if d1 and d2 do not have the same invocation lists; otherwise, false
And
Two delegates are equal if they are not null and are of exactly the same type, their invocation lists contain the same number of elements, and every element in the invocation list of the first delegate is equal to the corresponding element in the invocation list of the second delegate.
So if you compare an event to null it is true, as long as there are no elements in the invocation list.
Further notes
Some code have been subscribed to this event.
Allow me to claim that this is not true. If your event is never invoked (but your other conditions met) it means you nowhere have registered any callbacks to the event like e.g.
private void Start()
{
yourGameLevel.RefreshLevel += OnGameLevelRefreshed;
}
private void OnGameLevelRefreshed(object sender, LevelEventArgs args)
{
...
}
or it could simply mean that the GameLevel instance you registered the callbacks for is not the same as the one you are looking at.
If this is able to change (assuming that due to the GameLevel.current thing) you might want to rather make your event static since you anyway pass in the reference for the sender in case someone needs it.
public static event EventHandler<LevelEventArgs> RefreshLevel;
and then rather go
private void Start()
{
GameLevel.RefreshLevel += OnGameLevelRefreshed;
}
private void OnGameLevelRefreshed(object sender, LevelEventArgs args)
{
...
}
this way you can be sure that even if the current instance is changed/destroyed you are still receiving any of the invoked events.
if RefreshLevel != null, that means someone has subscribed to the event.
if RefreshLevel == null, that means no one has subscribed to the event.
Is it possible to assign the same method to multiple delegates all at once?
public class Hoge
{
public event Action EventA;
public event Action EventB;
public event Action EventC;
public Hoge()
{
EventA += () => FugaMethod();
EventB += () => FugaMethod();
EventC += () => FugaMethod();
}
private void FugaMethod()
{
Console.WriteLine("Hello.");
}
}
I'd like to simplify the assignments of the FugaMethod().
since events can't go as a parameter ... unfortunately ... no
if we are talking about a shitload of events, reflection would be the way to go ... but that i'd not really call "simplification"
edit:
for clarification:
what you can pass as a parameter:
the current list of eventhandlers attached to a static or specific event on a specific object (what you pass in this case is not the event, but a MulticastDelegate)
what you can not pass as a parameter: the event itself ...
you are not able to pass an event in a direct way that would allow to attach another event handler ... for that you would probably want to pass the specific obect instance, the EventInfo describing the event itself, and the new handler(s) ...
regarding "simplification":
what you need to do would be:
-use reflection to obtain the EventInfo objects of the desired events
-for each instance and each EventInfo call EventInfo.AddEventHandler, passing the instance as target and the eventhandler as handler
since you cannot pass the event as a parameter, you cannot extract a simple and typesafe method to get the desired EventInfo objects. you will have to use some selection by name, or other logic that takes the instance type apart by using Type.getEvent or Type.getEvents
so, if you are not handling a shitload of events, like a few hundred, writing it like you did seems to be the preferable way to go ... the reflection approach will be anything but simpler or shorter
There is a way to assign multiple event handlers at once that does not require reflection however itäs not trivial and some programming is necessary. You can use a dictionary to store your events if you want initialize them in a loop.
As a side note: by convention the event keyword should only be used if the delegate is of type EventHandler. It may and probably will confuse others when they try to use it.
Hoge class:
public class Hoge
{
// A dictionary to store your events.
private Dictionary<string, EventHandler> events = new Dictionary<string, EventHandler>()
{
{ "EventA", null },
{ "EventB", null },
{ "EventC", null }
};
// Event add/remove accessors.
public event EventHandler EventA
{
add
{
lock (events)
{
events["EventA"] += (EventHandler)events["EventA"] + value;
}
}
remove
{
lock (events)
{
events["EventA"] += (EventHandler)events["EventA"] - value;
}
}
}
// You can do the same for other events.
public event EventHandler EventB;
public event EventHandler EventC;
public Hoge()
{
// Initialize events in a loop.
foreach (var key in events.Keys.ToList())
{
events[key] += FugaMethod;
}
}
// Raises EventA.
public void RaiseEventA()
{
EventHandler handler;
if (null != (handler = (EventHandler)events["EventA"]))
{
handler(this, EventArgs.Empty);
}
}
// Event handler.
private void FugaMethod(object sender, EventArgs e)
{
Console.WriteLine("Hello.");
}
}
Usage:
class Program
{
static void Main(string[] args)
{
new Hoge().RaiseEventA();
}
}
How to: Use a Dictionary to Store Event Instances (C# Programming Guide)
Handling and Raising Events
I am new in c# development. I just trying to study the delegate feature. Based on the articles and notes I read about delegates, I tried to write a sample code to implement delegate based on what I understood from those notes and articles.
But I am getting an error while running the sample
"Object reference not set to an instance of an object."
What is the problem here ?. or Did I implemented the delegate in correct way ? or Is my concept about delegate is wrong ?..
Please help. Thanks in advance.
I posted my code below.
default.aspx.cs
public partial class _Default : System.Web.UI.Page
{
TestClass myObject = new TestClass();
protected void Page_Load(object sender, EventArgs e)
{
myObject.MyDelegateEvent += new TestClass.MyDelegate(myObject_MyDelegateEvent);
}
void myObject_MyDelegateEvent(object sender, EventArgs e)
{
Console.WriteLine("Delegate event called");
}
}
TestClass
public class TestClass
{
public delegate void MyDelegate(object sender, EventArgs e);
public event MyDelegate MyDelegateEvent;
public TestClass()
{
MyDelegateEvent(this, null); // Here getting error "Object reference not set to an instance of an object."
}
}
What you are trying is: raising the event in the constructor itself, i.e. at the time when there is no subscriber for your event hence MyDelegateEvent is null.
Best option is to null check before raising the event
//Check for not null
if(MyDelegateEvent != null)
{
MyDelegateEvent(this, null);
}
Always test for null before raising the event:
if (this.MyDelegateEvent != null)
{
// Raises the event here
}
If no handler is attached to your event when it is raised (which is the case here as it's raised in the constructor), a null reference exception can be thrown.
EDIT:
For the sake of completeness, when no event argument is used, you should use EventArgs.Empty instead of null:
if (this.MyDelegateEvent != null)
{
this.MyDelegateEvent(this, EventArgs.Empty);
}
Also, there is a EventHandler delegate that can be used when you do not specify any event argument. This is useful as you don't have to write your own each time.
When you declare an event, the event is initialised to null. So, in your TestClass constructor you attempt to fire the event but as the event is null this causes the NullReferenceException you are seeing.
To avoid this you need to register an event handler or check for null before raising the event:
if (MyDelegateEvent != null)
{
MyDelegateEvent(...)
}
Another trick is to initialise the event to an empty delegate when you create it which means you can skip the null checks.
public event MyDelegate MyDelegateEvent = delegate {}; // registers a delegate that does nothing
Minor point about some of the code above. It is possible for MyDelegateEvent to be changed between being checked and used. The recommended form (Framework Design Guidelines) is along the lines of
var del = MyDelegateEvent;
if (del != null) {
del(...);
}
hth,
Alan.
I looked at this example from the C# in nutshell book
(http://www.albahari.com/nutshell/ch04.aspx)
using System;
public class PriceChangedEventArgs : EventArgs
{
public readonly decimal LastPrice;
public readonly decimal NewPrice;
public PriceChangedEventArgs (decimal lastPrice, decimal newPrice)
{
LastPrice = lastPrice; NewPrice = newPrice;
}
}
public class Stock
{
string symbol;
decimal price;
public Stock (string symbol) {this.symbol = symbol;}
public event EventHandler<PriceChangedEventArgs> PriceChanged;
****protected virtual void OnPriceChanged (PriceChangedEventArgs e)
{
if (PriceChanged != null) PriceChanged (this, e);
}****
public decimal Price
{
get { return price; }
set
{
if (price == value) return;
OnPriceChanged (new PriceChangedEventArgs (price, value));
price = value;
}
}
}
class Test
{
static void Main()
{
Stock stock = new Stock ("THPW");
stock.Price = 27.10M;
// register with the PriceChanged event
stock.PriceChanged += stock_PriceChanged;
stock.Price = 31.59M;
}
static void stock_PriceChanged (object sender, PriceChangedEventArgs e)
{
if ((e.NewPrice - e.LastPrice) / e.LastPrice > 0.1M)
Console.WriteLine ("Alert, 10% stock price increase!");
}
}
What I don't understand, is why this convention is used...
****protected virtual void OnPriceChanged (PriceChangedEventArgs e)
{
if (PriceChanged != null) PriceChanged (this, e);
}****
Why do I need that method and why do I care to give it the "this" parameter?!? Cant I just attach the event from that class with the method PriceChanged in the test class straight away and skip that method?!?
You need the null check, since an event will be null until somebody subscribes to it. If you raise it directly and it's null, an exception will be thrown.
This method is used to raise the event, not to subscribe to it. You can subscribe to the event from another class easily:
yourObject.PriceChanged += someMethodWithTheAppropriateSignature;
However, when you want to have the event "fire", the class needs to raise the event.
The "this" parameter is providing the sender argument in the EventHandler<T>. By convention, delegates used for events have two parameters, the first is object sender, which should be the object that raised the event. The second is EventArgs or a subclass of EventArgs, which provides the information specific to that event. The method is used to properly check for null and raise the event with the appropriate information.
In this case, your event is declared as:
public event EventHandler<PriceChangedEventArgs> PriceChanged;
EventHandler<PriceChangedEventArgs> is a delegate which has a signature of:
public delegate void EventHandler<T>(object sender, T args) where T : EventArgs
This means that the event must be raised with two parameters - an object (the sender, or "this"), and an instance of PriceChangedEventArgs.
That being said, this convention is not actually the "best" way to raise the event. It would actually be better to use:
protected virtual void OnPriceChanged (PriceChangedEventArgs e)
{
var eventHandler = this.PriceChanged;
if (eventHandler != null)
eventHandler(this, e);
}
This protects you in multi-threaded scenarios, since it's possible that a single subscribe could actually unsubscribe in between your null check and the raise if you have multiple threads operating.
This is a convenience for invoking the event.
You do need to check that the event has subscribers, and it is typical to pass this as the sender of the event.
Because the same handler can be used for multiple events, passing an instance of the sender is the only way that you could reliable unsubscribe from the event once it has fired.
I think the preferred way to invoke is to assign to a variable first, lest PriceChanged become null after checking, but before invoking:
var handler = PriceChanged;
if(handler != null) handler(this, e);
Null checks are used since a (event) delegate list is not empty but null if there are no subscribers.
However, it's not thread safe. So it can blow up in your face if you start using a BackgroundWorker or any other multi-threaded technique.
I suggest that you use an empty delegate instead:
public event EventHandler<PriceChangedEventArgs> PriceChanged = delegate {};
Since it allows you to just write:
protected virtual void OnPriceChanged (PriceChangedEventArgs e)
{
PriceChanged (this, e);
}
It's thread safe and the code is more easy to read.
why do I care to give it the "this" parameter?!?
Same event handler might be used by multiple event generators. The sender tells which generate the invocation is for. You should always send the correct event generator as it is expected and you'll break open/closed principle if you don't
Why Do I need that method?
You don't, unless you would duplicate code otherwise (such as generating the EventArgs class)
I am sure that I am just not understanding something fundamental about events and/or delegates in C#, but why can't I do the Boolean tests in this code sample:
public class UseSomeEventBase {
public delegate void SomeEventHandler(object sender, EventArgs e);
public event SomeEventHandler SomeEvent;
protected void OnSomeEvent(EventArgs e) {
// CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
if (SomeEvent != null) SomeEvent(this, e);
}
}
public class UseSomeEvent : UseSomeEventBase {
public bool IsSomeEventHandlerNull() {
// "LEFT HAND SIDE" COMPILER ERROR
return SomeEvent == null;
}
}
class Program {
static void Main(string[] args) {
var useSomeEvent = new UseSomeEvent();
useSomeEvent.SomeEvent +=new UseSomeEventBase.SomeEventHandler(FuncToHandle);
// "LEFT HAND SIDE" COMPILER ERROR
if (useSomeEvent.SomeEvent == null) {
}
var useSomeEventBase = new UseSomeEventBase();
useSomeEventBase.SomeEvent += new UseSomeEventBase.SomeEventHandler(FuncToHandle);
// "LEFT HAND SIDE" COMPILER ERROR
if (useSomeEventBase.SomeEvent == null) {
}
}
static void FuncToHandle(object sender, EventArgs e) { }
}
An event is really just an "add" operation and a "remove" operation. You can't get the value, you can't set the value, you can't call it - you can just subscribe a handler for the event (add) or unsubscribe one (remove). This is fine - it's encapsulation, plain and simple. It's up to the publisher to implement add/remove appropriately, but unless the publisher chooses to make the details available, subscribers can't modify or access the implementation-specific parts.
Field-like events in C# (where you don't specify the add/remove bits) hide this - they create a variable of a delegate type and an event. The event's add/remove implementations just use the variable to keep track of the subscribers.
Inside the class you refer to the variable (so you can get the currently subscribed delegates, execute them etc) and outside the class you refer to the event itself (so only have add/remove abilities).
The alternative to field-like events is where you explicitly implement the add/remove yourself, e.g.
private EventHandler clickHandler; // Normal private field
public event EventHandler Click
{
add
{
Console.WriteLine("New subscriber");
clickHandler += value;
}
remove
{
Console.WriteLine("Lost a subscriber");
clickHandler -= value;
}
}
See my article on events for more information.
Of course the event publisher can also make more information available - you could write a property like ClickHandlers to return the current multi-cast delegate, or HasClickHandlersto return whether there are any or not. That's not part of the core event model though.
You can easily use a very simple approach here to not repeatedly subscribe to an event.
Either of the 2 approaches below can be used:
Flag approach : _getWarehouseForVendorCompletedSubscribed is a private variable initialized to false.
if (!_getWarehouseForVendorCompletedSubscribed)
{
_serviceClient.GetWarehouseForVendorCompleted += new EventHandler<GetWarehouseForVendorCompletedEventArgs>(_serviceClient_GetWarehouseForVendorCompleted);
_getWarehouseForVendorCompletedSubscribed = true;
}
Unsubscribe Approach :Include an unsubscribe everytime you want to subscribe.
_serviceClient.GetWarehouseForVendorCompleted -= new
EventHandler<GetWarehouseForVendorCompletedEventArgs>
(_serviceClient_GetWarehouseForVendorCompleted);
_serviceClient.GetWarehouseForVendorCompleted += new
EventHandler<GetWarehouseForVendorCompletedEventArgs>
(_serviceClient_GetWarehouseForVendorCompleted);
Here the answer:
using System;
delegate void MyEventHandler();
class MyEvent
{
string s;
public event MyEventHandler SomeEvent;
// This is called to raise the event.
public void OnSomeEvent()
{
if (SomeEvent != null)
{
SomeEvent();
}
}
public string IsNull
{
get
{
if (SomeEvent != null)
return s = "The EventHandlerList is not NULL";
else return s = "The EventHandlerList is NULL"; ;
}
}
}
class EventDemo
{
// An event handler.
static void Handler()
{
Console.WriteLine("Event occurred");
}
static void Main()
{
MyEvent evt = new MyEvent();
// Add Handler() to the event list.
evt.SomeEvent += Handler;
// Raise the event.
//evt.OnSomeEvent();
evt.SomeEvent -= Handler;
Console.WriteLine(evt.IsNull);
Console.ReadKey();
}
}
Here's a slightly different question
What value is there in testing an externally defined event for null?
As an external consumer of an event you can only do 2 operations
Add a handler
Remove a handler
The null or non-nullness of the event has no bearing on these 2 actions. Why do you want to run a test which provides no perceivable value?
It's a rule in place when using the 'event' keyword. When you create an event, you are restricting outside class interaction with the delegate to a "subscribe / unsubscribe" relationship, this includes cases of inheritance. Remember an event is essentially a property, but for method calls, it isn't really an object itself, so really it looks more like this:
public event SomeEventHandler SomeEvent
{
add
{
//Add method call to delegate
}
remove
{
//Remove method call to delegate
}
}
You'd have to do that from the base class. That's the exact reason that you did this:
protected void OnSomeEvent(EventArgs e) {
// CANONICAL WAY TO TEST EVENT. OF COURSE, THIS WORKS.
if (SomeEvent != null) SomeEvent(this, e);
}
You can't access events from a derived class. Also, you should make that method virtual, so that it can be overridden in a derived class.
Publisher of the event implicitly overload only += and -= operations, and other operations are not implemented in the publisher because of the obvious reasons as explained above, such as don't want to give control to subscriber to change events.
If we want to validate if a particular event is subscribed in the subscriber class, better publisher will set a flag in its class when event is subscriber and clear the flag when it is unsubscriber.
If subscriber can access the flag of publisher, very easily identifiable whether the particular event is subscriber or not by checking the flag value.