Can anyone explain why this code fails when using ?
operator but works with if statement?
The code below compiles without errors. It throws an exception when I run it.
var myClass = new MyClass<string>();
string tString;
//this works fine
if (myClass.HasValue)
{
tString = myClass;
}
else
{
tString = null;
}
//this throws Object reference not set to an instance of an object.
tString = myClass.HasValue ? myClass : null;
class MyClass<T>
{
private T value;
public T Value
{
get
{
if(value == null)
{
throw new Exception("Value cannot be null");
}
return value;
}
set { this.value = value; }
}
public static implicit operator T(MyClass<T> x)
{
return x.value;
}
public bool HasValue
{
get { return value != null; }
}
}
tString = myClass.HasValue ? myClass : null;
Here, we've got a ternary. A ternary has a type, which is determined from the two arguments. The compiler looks at myClass
and null
, sees that they're compatible (they can both be converted to a MyClass<string>
), and determines that the type of the ternary is MyClass<string>
.
If myClass.HasValue
is false
, then we hit the null
branch of the ternary. We then get ourselves a MyClass<string>
instance which is null
. We then need to convert that to a string
: to do this, the compiler calls your implicit operator but passes in null
. That causes your NullReferenceException
because you access x.value
but x
is null
.
This doesn't happen with the if/else
because we never construct a MyClass<string>
which is null
. Instead, we assign null
directly to the string
.
This simpler example causes the same exception for the same reason:
MyClass<string> myClass = null;
string s = myClass;
You can also see this by forcing the type of the ternary to be string
rather than MyClass<string>
, by doing:
tString = myClass.HasValue ? myClass : (string)null;
or
tString = myClass.HasValue ? (string)myClass : null;
In this case the exception doesn't occur.
The OP commented that this doesn't happen for an explicit operator. This is wrong: it does.
tString = (string)(myClass.HasValue ? myClass : null);
This throws the same exception, for the same reason.
If instead you do:
tString = myClass.HasValue ? (string)myClass : null;
Then you're falling into the same case as I described earlier, because you never create a MyClass<string>
which is null, and so you never try and convert this null
MyClass<string>
into a string
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With