I asked this question. This code doesn't compile ("Cannot convert Generic<T>
to T
") because of the reason explained here (even if I'd expect an InvalidCastException
at run-time instead of a compile-time error).
class NonGeneric
{
}
class Generic<T> : NonGeneric
where T : NonGeneric
{
T DoSomething()
{
return (T)this; // ** Cannot convert...
}
}
Accepted solution gave this workaround:
T DoSomething()
{
return this as T;
}
My question is: why? as
operator should be exactly equivalent to cast operator:
The as operator is like a cast operation. However, if the conversion isn't possible, as returns null instead of raising an exception.
If this as T
should be equivalent to this is T? (T)this: (T)null
then why as T
works and (T)this
doesn't even compile? AFAIK cast could be used in a more wide range of situations than as
:
Note that the as operator performs only reference conversions, nullable conversions, and boxing conversions. The as operator can't perform other conversions, such as user-defined conversions, which should instead be performed by using cast expressions.
Then why this? Is it a documented feature of as
operator? Is it a compiler/language limitation with generic types? Note that this code compiles fine:
return (T)((object)this);
Is this because compiler can't be sure if T
is dynamic
(even if there is a where
constraint) then it'll always generate such code?
It says in the C# Language Specification (emphasis mine),
If the compile-time type of E is not dynamic, the operation E as T produces the same result as E is T ? (T)(E) : (T)null except that E is only evaluated once. The compiler can be expected to optimize E as T to perform at most one dynamic type check as opposed to the two dynamic type checks implied by the expansion above.
If the compile-time type of E is
dynamic
, unlike the cast operator theas
operator is not dynamically bound (§7.2.2). Therefore the expansion in this case is:E is T ? (T)(object)(E) : (T)null
This seems to be the reason why the compilation succeed using as
or when this
is cast to an object first. Furthermore,
In an operation of the form
E as T
,E
must be an expression andT
must be a reference type, a type parameter known to be a reference type, or a nullable type. Furthermore, at least one of the following must be true, or otherwise a compile-time error occurs:• An identity (§6.1.1), implicit nullable (§6.1.4), implicit reference (§6.1.6), boxing (§6.1.7), explicit nullable (§6.2.3), explicit reference (§6.2.4), or unboxing (§6.2.5) conversion exists from
E
toT
.• The type of
E
orT
is an open type.•
E
is thenull
literal.
Which is the current case with your generic class.
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