According to C# Reference
"The null keyword is a literal that represents a null reference, one
that does not refer to any object. null is the default value of
reference-type variables'
I was surprised to find out that
Commenting e=null line in the following app code (taken from the article "Difference Between Events And Delegates in C#") results in compilation error:
Use of unassigned local variable 'e'
while without commenting it is compiled and run.
I do not get:
Where is variable e used?
Is it possible to force the app to run without the dumb assigning the variable to null?
f
using System;
class Program
{
static void Main(string[] args)
{
DelegatesAndEvents obj = new DelegatesAndEvents();
obj.Execute();
}
}
public class DelegatesAndEvents
{
public event EventHandler MyEvent;
internal void Execute()
{
EventArgs e;
//Commenting the next line results in compilation error
//Error 1 Use of unassigned local variable 'e'
e = null;
int sender = 15;
MyEvent += MyMethod;
MyEvent += MyMethod2;
MyEvent(sender, e);
}
void MyMethod(object sender, EventArgs e)
{
Console.WriteLine(sender);
}
void MyMethod2(object sender, EventArgs e)
{
Console.WriteLine(sender);
Console.ReadLine();
}
}
Update (or the comment to all answers):
So, and I never knew it, there are to kind of nulls - one which is assigned and another which is not assigned... Funny...
They should probably have different types, for checking:
if typeof(unassigned-null) then do this;
if typeof(assigned_null) then do that;
Local variables are not initialized with their default value, whereas fields are.
There's no way for the compiler to know, when compiling Execute what event handlers are registered to MyEvent. (Others may have already been registered before Execute is called, and they may attempt to use e). As such, it doesn't attempt any analysis on any handlers it could know are registered. So, since you want to pass e, it has to be initialized.
If you want to pass null, why introduce e at all? Just have
MyEvent(sender, null);
Which at least makes your intent explicit.
You are using e without assigning a value to it. Using e = null;, you are assigning value to variable e as null is also a value.
Also, according to C# reference default value for reference types is null but here e is a variable. Variables are not defaulted, and must have "definite assignment" before they are used.
To answer your questions:
Question: Where is variable e used?
Answer: It is used where you pass it to MyEvent(sender, e);
Question: Is it possible to force the app to run without the dumb assigning the variable to null?
Answer: No, and it's not dumb because you ARE using e by passing it to another method. You are just not using it directly - but you are still using it.
Actually, you don't need to declare e anywhere - you can just pass null directly to MyEvent:
MyEvent(sender, null);
Then you don't need to do any redundant variable declaration.
Related
Quite new to C#, from a long time ago C++ background and so I seem to be having my trouble transitioning away from pointers to ref in C#.
I have a class (EColour) which I create using the constructor shown.
I assign (or at least try to) a reference to cellTemplate to the variable m_template.
Looking in debug, at the time of construction, m_template is most definitely NOT null.
However, by the time I come to handle OnMouseClick event, I get a null exception error because m_template has magically turned to null.
Could anyone please shed light on what I have done wrong and how to fix it?
public EColour(ref ICellTemplate cellTemplate)
{
m_template = (ColourTemplate)cellTemplate;
}
protected override void OnMouseClick(DataGridViewCellMouseEventArgs e)
{
ColorDialog dlg = new ColorDialog();
dlg.AnyColor = m_template.AnyColour; // This throws an exception because m_template is null
base.OnMouseClick(e);
}
ColourTemplate m_template;
In C# we have two main kinds of types:
value type - its all digit types (int, float, double, long ...)
reference type -its types that inherited from object
ICellTemplate is a reference class. So what you need - just send it in argument as a regular variable.
public class EColour
{
private ColourTemplate m_tamplate;
public EColour(ICellTemplate cellTemplate)
{
m_template = (ColourTemplate)cellTemplate;
}
}
This is the original source-code written in C#
public delegate Unit UnitResolveEventHandler(object sender, ResolveEventArgs args);
public event UnitResolveEventHandler UnitResolve;
public static Unit GetUnitByName(string name) {
Instance.unitsByName.TryGetValue(name, out result);
if (Instance.UnitResolve != null) {
foreach (UnitResolveEventHandler handler in Instance.UnitResolve.GetInvocationList()) {
result = handler(Instance, new ResolveEventArgs(name));
}
}
}
Using an online translator, I get this VB.NET code:
Public Delegate Function UnitResolveEventHandler(sender As Object, args As ResolveEventArgs) As Unit
Public Event UnitResolve As UnitResolveEventHandler
Public Shared Function GetUnitByName(name As String) As Unit
Instance.unitsByName.TryGetValue(name, result)
If Instance.UnitResolve IsNot Nothing Then
For Each handler As UnitResolveEventHandler In Instance.UnitResolve.GetInvocationList()
result = handler(Instance, New ResolveEventArgs(name))
Next
End If
End Function
The compiler marks the event declaration with this error message:
Events cannot be declared with a delegate type that has a return type.
And the Instance.UnitResolve calls inside the GetUnitByName() method with this error message:
Public Event UnitResolve As UnitResolveEventHandler' is an event, and
cannot be called directly.
How can I properly translate the code from C# to VB.NET without losing functionality?
The customary way of returning a value from an event handler to the invocation of the event is through an argument---either a member of the event arguments class, or through a ByRef parameter on the delegate.
If you have control over ResolveEventArgs and the event handler routines, you could do something like this:
Public Class ResolveEventArgs
'...
Public ReturnValue As Unit
'...
End Class
In the body of your handler (assuming the typical declaration of the event arguments as e), instead of Return (return value):
e.ReturnValue = (return value) 'substitute for (return value) as appropriate
Then, the body of your For Each loop would look like this:
Dim args As New ResolveEventArgs(name)
handler(Instance, args)
result = args.ReturnValue
As an aside, the original C# code has a thread-safety issue. It could throw a NullReferenceException in the event that the last subscribed handler is removed between the null check and reading the invocation list. Whether this is serious (or a concern at all) depends on where and how it's being used. The usual way of addressing this is to store to a temporary, then do the null check and invocation list on the temporary. If you're using a recent version of the .NET languages, then you can skip the null check and use the ?. operator, which should also be safe against the particular thread-safety issue.
The original C# source code is bad; event handlers shouldn’t return values. You’ll have to make it not-an-event:
Public UnitResolve As UnitResolveEventHandler
and use Delegate.Combine manually to add event handler:
Instance.UnitResolve = Delegate.Combine(Instance.UnitResolve, newHandler)
I have a ToolStripMenuItem that I want to declare and instantiate with a String, a null value for an image, and an Event Handler for its Click event. This is the format Intellisense is expecting:
ToolStripMenuItem(string text, System.Drawing.Image image, EventHandler onClick).
But I am unable to assign the Event Handler and I do not know the proper syntax to do so. As a workaround, I assign the .Click event in the constructor like so...
class Timer
{
//The other WinForms objects and my methods are omitted.
private ToolStripMenuItem StartButton = new ToolStripMenuItem("Start Timer");
public Timer()
{
//I want the assignment of StartButton_Click in my declaration and initialization of StartButton, not here.
StartButton.Click += new EventHandler(StartButton_Click);
}
public void StartButton_Click(object sender, EventArgs e)
{
//The logic here is not relevant.
}
}
I tried the syntax below but I keep getting the error:
"CS0236 A field initializer cannot reference the non-static field, method, or property 'Timer.StartButton_Click(object, EventArgs)'"
new ToolStripMenuItem("Start Timer", null, new EventHandler(StartButton_Click));
Intelliense suggests I use the format
EventHandler(void(object,EventArgs)target)
but I do not know how to fill out the expected syntax property. How do I write the declaration of StartButton so that the method StartButton_Click is called after a Click event?
The correct place to instantiate it is in the constructor. Do it all at once, like this:
private ToolStripMenuItem StartButton;
public Timer()
{
StartButton = new ToolStripMenuItem("Start Timer", null, StartButton_Click);
}
As for that compiler error, you can read more about it here, although it's sparse on the details.
From Stack Overflow: You cannot use an instance variable to initialize another instance variable. Why? Because the compiler can rearrange these - there is no guarantee that reminder will be initialized before StartButton_Click, so the above line might throw a NullReferenceException.
Make the method static and you should be good to go.
What is the difference between these two code samples for invoking an event?
Sample 1
public void OnDataChanged()
{
if (DataChanged != null)
{
DataChanged(this);
}
}
Sample 2
DataChanged.Invoke(this);
When should I use each method to invoke a custom event? And why sometimes I get a NullReferenceException when I try to invoke event using DataChanged.Invoke(this), but when I convert the event invocation to the method in Sample 1 the DataChanged never becomes null anymore?
An OnXYZ method should always follow this form:
public void OnXYZ()
{
var evt = XYZ;
if (evt != null)
evt(sender, e); // where to get e from differs
}
There are a couple of reasons for this form:
The if evt != null check ensures that we don't try to invoke a null delegate. This can happen if nobody has hooked up an event handler to the event.
In a multithreaded scenario, since delegates are immutable, once we've obtained a local copy of the delegate into evt, we can safely invoke it after checking for non-null, since nobody can alter it after the if but before the call.
What to pass for e differs, if you need to pass an EventArgs descendant with a parameter there are two ways:
public void OnXYZ(string p)
{
var evt = XYZ;
if (evt != null)
evt(sender, new SomeEventArgs(p));
}
or more commonly this:
public void OnXYZ(SomeEventArgs e)
{
var evt = XYZ;
if (evt != null)
evt(sender, e);
}
This syntax:
evt(sender, e);
is just a different way of writing this:
evt.Invoke(sender, e);
Also note that externally to your class, the event is an event, you can only add or remove event handlers from it.
Internal to your class, the event is a delegate, you can invoke it, check the target or method, walk the list of subscribers, etc.
Also, in C# 6 there is a new operator introduced, ?. - the Null-conditional operator - which is basically short for if not-null, dereference, which can shorten this method:
public void OnXYZ(SomeEventArgs e)
{
var evt = XYZ;
if (evt != null)
evt(sender, e);
}
into this:
public void OnXYZ(SomeEventArgs e)
{
XYZ?.Invoke(sender, e);
}
which can be further shortened with the use of Expression-bodied members:
public void OnXYZ(SomeEventArgs e) => XYZ?.Invoke(sender, e);
Please note that it is not possible to write this:
XYZ?.(sender, e);
so you must in this case use Invoke yourself.
when I convert the event invocation to the method in Sample 1 the DataChanged never becomes Null
Then you're simply looking at two different scenarios.
if you don't declare an event like public event EventHandler YourEvent = delegate { };, then YourEvent is null until some consumer subscribes to it.
If nothing has subscribed to DataChanged it will be set to null, and so when you attempt to do DataChanged.Invoke(this) you'll get a NullRefException as really it's trying to do null.Invoke(this). The reason for the additional if (DataChanged != null) is to avoid this occurring when nobody has subscribed to the event.
I don't believe that when you use Sample 1 DataChanged is never null, it's just never reaching the .Invoke to throw up the exception. It will always be null if nobody has subscribed.
Are you sure, that in Sample 1 the DataChanged is never null? Or you just don't get the NullReference Exception (because you check if DataChanged is not null in the if statement)?
Let's start with the basics. Event is a special kind of delegate. When you call DataChanged(this) and DataChanged.Invoke(this), it's the same thing. Why? Because it compiles to the same thing. So all in all the DataChanged(this) is just a shorthand for calling DataChanged.Invoke(this).
Now, why do we need to check for null reference (like in sample 1).
Basically, when you invoke an event, you invoke all methods that were subscribed to this event (e.g. DataChanged += someEventHandler).
If nobody subscribed to this event, it will have a null value. No method was assigned to handle this event. In other words: the event handler is null.
That's why it's a good practice to check for null, before invoking an event.
An Example:
public void OnAbc(){
var data=Abc;
if(!String.IsNullOrEmpty(data))
Abc(sender,e);
}
I just wrote some code that I don't quite comprehend how it works.
My question is about the local variables in the for loop which are then referenced when the radio button event occurs.
How does it track the different "versions" of these local variables and operate correctly? (i.e. the resulting radio buttons each fire an event with their corresponding value which is derived from an outer local variable)
public class RadioButtonPanel<T> : FlowLayoutPanel
{
public RadioButtonPanel()
{
foreach (object value in Enum.GetValues(typeof(T)))
{
string name = Enum.GetName(typeof(T), value);
var radioButton = new RadioButton { Text = name };
radioButton.CheckedChanged += (s, e) =>
{
if (radioButton.Checked && this.Selected != null)
Selected((T)Enum.Parse(typeof(T), name));
};
this.Controls.Add(radioButton);
}
}
public event SelectedEvent Selected;
public delegate void SelectedEvent(T t);
}
This is done via a Closure.
Basically you can imagine that a small class has been created for you on your behalf, that has two properties for your local variables, and a single function. When your lambda gets called, it basically news one of those up and calls the function, thus preserving the values given to it.
The C# specification actually has some really good examples of how this is done by the compiler. Specifically section 6.5.3
They're called closures, see Wikipedia: http://en.wikipedia.org/wiki/Closure_(computer_science)
Basically, they allow for the use of non-local variables within the lambdas. From what I can remember, these variables are compiled externally from your function so they can be used globally.