How can I implement a struct so that the following cast can be performed?
var a = new StatusedValue<double>(1, false);
var b = (StatusedValue<int>)a;
My implementation should behave similarly to Nullable<T>
, which works fine. However, this code fails with System.InvalidCastException
:
public struct StatusedValue<T> where T : struct
{
public StatusedValue(T value) : this(value, true)
{
}
public StatusedValue(T value, bool isValid)
{
this.value = value;
this.isValid = isValid;
}
private T value;
private bool isValid;
public static implicit operator StatusedValue<T>(T value)
{
return new StatusedValue<T>(value);
}
public static explicit operator T(StatusedValue<T> value)
{
return value.value;
}
}
Result:
Unable to cast object of type 'StatusedValue`1[System.Double]' to type 'StatusedValue`1[System.Int32]'.
This works for Nullable<T>
types because they get special treatment from the compiler. This is called a "lifted conversion operator", and you cannot define your own.
From section 6.4.2 of the C# Specification:
6.4.2 Lifted conversion operators
Given a user-defined conversion operator that converts from a non-nullable value type S to a non-nullable value type T, a lifted conversion operator exists that converts from S? to T?. This lifted conversion operator performs an unwrapping from S? to S followed by the user-defined conversion from S to T followed by a wrapping from T to T?, except that a null valued S? converts directly to a null valued T?. A lifted conversion operator has the same implicit or explicit classification as its underlying user-defined conversion operator. The term “user-defined conversion” applies to the use of both user-defined and lifted conversion operators
If you're happy calling a method, try
public StatusedValue<U> CastValue<U>() where U : struct
{
return new StatusedValue<U>((U)Convert.ChangeType(value, typeof(U)), isValid);
}
This will unfortunately throw at runtime rather than compile time if T
cannot be converted to U
.
Edit: As pointed out below, if you constrain to IConvertible
as well as/instead of struct
then every conversion is theoretically possible at compile time, and you'll only get a runtime failure because of bad runtime values.
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