Reputation: 59
Say I have a class
class T
{
int num;
public T(int num)
{
this.num = num;
}
public void p()
{
Console.WriteLine(this.num);
}
}
And I have a null object T t = null
. Calling the method t.p()
will cause a NullReferenceException no matter what.
Is it not possible to test if the object is null (by using this == null
(to check if the object is null within the method)), so is there a way to set a default method that will work on a null object?
Upvotes: 2
Views: 2410
Reputation: 57952
In C/C++ you can do this, but not (easily) in C#.
For example, you might want to have a check for "if a string is empty" or "if a collection is empty", and treat a null object as meaning "empty". For this sort of case, being able to call an IsEmpty()
method via a null pointer/reference results in what seems like more elegant/readable code:
if (myList.IsEmpty())
...
rather than
if (myList == null || myList.IsEmpty())
...
However, it is also extremely dangerous as it hides unusual/unexpected behaviour, and encourages the caller to stop checking for nulls. As you cannot guarantee that every method you call will protect you from nulls, how do you know that it is safe to stop checking for nulls in the calling code? Or do you check for null twice (in both the calling and called code), just in case? If you are calling many methods on the same object instance, wouldn't it be far more efficient to check for null once at the start?
So although this pattern can be used in some languages, it is not considered a good practice.
This is (one reason) why, in C#, such code is not allowed. Instead the preferred pattern is to provide a static method to check the status:
if (string.IsNullOrEmpty(myString))
....
This is only slightly less readable, but much safer as the null check is clearly visible to the reader. Even without "IsNull" in the title, tou can see that the reference is being passed to another method, and it is more reasonable to assume that the called method will be well written and check for nulls. Being transparent and explicit makes code much easier for others to read.
C# also provides the null coalescing ??
operator to make the null checks cleaner in the calling code. If you want a default to use if the object is null, then you can use this form:
string result = myString ?? string.Empty;
...which in this case will return myString
if it is non-null, or string.Empty if it is null, thus guaranteeing that result
is never null.
Finally, in C# 6 you can use the ?.
operator, which combines an "if not null" check with an access, so you can replace:
if (element != null && element.FirstChild != null)
element.FirstChild.DoSomething();
with
element?.FirstChild?.DoSomething();
This is functionally equivalent, but a much more compact syntax.
Upvotes: 1
Reputation: 14904
Calling an instance method on a null
reference will always result in a NullReferenceException
. You have to check if the reference is not null
or use the C# 6 ?.
operator to do it for you.
T t = null;
t?.p(); // no method is called
You can however use an extension method on a null
reference. It has some limitations, as you can only access public members in an extension method, but might be useful (especially if you cannot use C# 6 and ?.
):
class T
{
public int Number { get; private set; } // public property
public T(int number)
{
Number = number;
}
}
static class Extensions
{
public static void PrintT(this T t)
{
if (t == null) Console.WriteLine("null");
else Console.WriteLine(t.Number);
}
}
T t = null;
t.PrintT(); // no exception, writes "null" to the console
It is possible to call an instance method on a null
reference in CIL, simply using call
instead of callvirt
. The latter checks the run-time type of the referenced object, that's why it throws NRE immediately. call
should call the method normally, but with the implicit this
parameter set to null, and throw on first access to any member of this
.
Edit
The System.Reflection.Emit
namespace in C# contains types that let you dynamically emit IL. You can use it to call an instance method with a call
instead of callvirt
. (I modified code posted in this answer to work for methods. The sample here will only work for parameterless methods)
public static void MakeNonVirtualCall<T>(T c, Expression<Action<T>> f)
{
var expression = f.Body as MethodCallExpression;
if (expression == null) throw new ArgumentException();
var dyn = new DynamicMethod("NVCall", null, new[] { typeof(T) }, typeof(T).Module, true);
var il = dyn.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Call, expression.Method);
il.Emit(OpCodes.Ret);
((Action<T>)dyn.CreateDelegate(typeof(Action<T>)))(c);
}
Now, assuming we have a class
class Class
{
public void Method()
{
if (this == null) Console.WriteLine("`this` is null");
else Console.WriteLine("`this` is not null");
}
}
We can use it as
Class nullRef = null;
Class instance = new Class();
MakeNonVirtualCall(nullRef, t => t.Method()); // no exception, prints "`this` is null"
MakeNonVirtualCall(instance, t => t.Method()); // prints "`this` is not null"
It works - an instance method in a C# class can be called on a null
reference from IL (but still not directly from C#).
Note that the MSDN documentation is incorrect here:
Calls to an instance (or virtual) method must push that instance reference before any of the user-visible arguments. The instance reference must not be a null reference.
Upvotes: 1
Reputation: 495
No you can't. And I'll tell you why. The keyword this
refers to whichever object is accessing a variable or calling a method. So a this
inside doSomething()
would refer to object
in object.doSomething()
.
Now coming to your question about null
, when you write T t = null;
it means you made a variable of type T
which REFERS to NOTHING!!! And by nothing I mean, wait for it, NOTHING! Can you call methods from nothingness? NO!
Upvotes: 0
Reputation: 36513
Just check for null
in the calling code.
T t = null;
// somewhere further..
if (t == null)
//t is null, don't use it
else
//save to use t
Another way (if you don't expect the object to be null
is to use try
/catch
):
T t = null;
// somewhere further..
try {
t.p();
}
catch(NullReferenceException ex) {
// t is null
}
Upvotes: 0
Reputation:
In whatever code that calls t.p()
you should check if t
is null
. You shouldn't be calling methods on a null
instance.
Regarding the second part of your question, no it is not possible to set a default method that will be called if the instance of that object is null.
I should point out that there is also the Null Object Pattern, that, depending on the exact circumstances of usage may help. I would not use it as replacement for null checks though. I've found the pattern useful in some cases.
Upvotes: 3