RexMan85
RexMan85

Reputation: 59

Using a method for a null class

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

Answers (5)

Jason Williams
Jason Williams

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

Jakub Lortz
Jakub Lortz

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

vlence
vlence

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

Hatted Rooster
Hatted Rooster

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

user147373
user147373

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

Related Questions