Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I test a generic for null when it may not be nullable or may not be an object?

Tags:

c#

generics

While writing a class that contains a generic variable

public class ServiceInvoker<TService> : IDisposable
{
    private TService _Service;

    public ServiceInvoker()
    {
        _Service = Activator.CreateInstance<TService>();
    }

    public void Invoke(Action<TService> action)
    {
        // CAN use null
        if (_Service == null)
            throw new ObjectDisposedException("ServiceInvoker");

        ....
    }

    public void Dispose()
    {
        // CAN'T use null
        this._Service = default(TService);
    }
}

I noticed that the compiler allows me to check for null on a generic variable, but that, of course, don't allow me to set it to null, hence why we have to use default(TService).

Shouldn't the compiler warn me that I use null? Or is it using boxing conversion to an object to do a test for null here?

I read about the proper null evaluation for generic, but I'm interested in knowing why, instead of just how.

like image 511
Pierre-Alain Vigeant Avatar asked Feb 22 '11 16:02

Pierre-Alain Vigeant


People also ask

Why null safety is important for programming?

Null safety is a major new productivity feature that helps you avoid null exceptions, a class of bugs that are often hard to spot. As an added bonus, this feature also enables a range of performance improvements.

When to use nullable types in C#?

We are using nullable types when we need to represent an undefined value of an underlying type. While Boolean values can have either true or false values, a null in this case means false as there is no undefined value. When you have a database interaction, a variable value can be either undefined or missing.

What is a nullable type and what purpose does it serve?

The Nullable type allows you to assign a null value to a variable. Nullable types introduced in C#2.0 can only work with Value Type, not with Reference Type. The nullable types for Reference Type is introduced later in C# 8.0 in 2019 so that we can explicitly define if a reference type can or can not hold a null value.

Can a generic be null?

Also, we cannot simply return null from a generic method like in normal method. Below is the error that a generic method will throw if we are trying to return null. So, to return a null or default value from a generic method we can make use default().


1 Answers

Why can I test a generic for null when it may not be nullable or may not be an object?

The question is ill-posed; the value of any expression of generic type will always be either an object or a null reference at runtime. I think you meant to ask:

Why can I test a generic for null when it its runtime type might be neither a nullable value type nor a reference type?

The C# specification guarantees that equality comparison against the literal null is legal in section 7.6.10, which I quote here for your convenience:


If an operand of a type parameter type T is compared to null, and the run-time type of T is a value type, the result of the comparison is false. [...] The x == null construct is permitted even though T could represent a value type, and the result is simply defined to be false when T is a value type.


Note that there is a small error in the spec here; the last sentence should end "non-nullable value type."

I'm never sure if I've actually answered a "why?" question satisfactorily or not. If that's not satisfactory, try asking a more specific question.

Shouldn't the compiler warn me that I use null?

No. Why do you believe that the compiler should warn you for using a feature of the language correctly?

is it using boxing conversion to an object to do a test for null here?

Well, that's a bit of a tricky point. On the one hand, section 7.6.10 states:


The predefined reference type equality operators never cause boxing operations to occur for their operands. It would be meaningless to perform such boxing operations, since references to the newly allocated boxed instances would necessarily differ from all other references.


However, when we generate IL for the comparison of a generic T to null, we of course actually do generate a box of T, a load of null, and a comparison. The jitter can be smart enough to elide the actual memory allocation of the boxing; if T is a reference type then it needs no boxing, if it is a nullable value type then it could be turned into a call to check the HasValue property, and if it is a non-nullable value type then the boxing and check can be turned into simply generating "false". I do not know exactly what the various different jit compiler implementations actually do; if you're interested, check and see!

like image 186
Eric Lippert Avatar answered Oct 12 '22 13:10

Eric Lippert