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.
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!
The CLR knows that a non-nullable value type can never be null, so it probably just always returns false
. Is that what you're asking?
This compiles, though with a warning:
int l = 0;
if (l == null) {
//whatever
}
The warning is this:
warning CS0472: The result of the expression is always 'false'
since a value of type 'int' is never equal to 'null' of type 'int?'
So it works, though with not-very-useful results. With a generic, though, that could be a useful test.
Why do you think the compiler should warn you? The compiler will give warnings when you are using something like this:
if (1 == 2) //always false
if (Dog is Cat) //never true
But in this case, the condition is possible to be true(when TService is a refernece type and it's null), or false(when it's a value type, or a reference type but not null). It perfectly represents the usage of if statement(sometimes true, and sometimes false).
精彩评论