I have just posted an answer to this question but I'm not entirely convinced of my answer.There are two things I'm wondering, consider this code:
class Foo<T>
{
void SomeMethod()
{
string str = "foo";
Foo<T> f = str as Foo<T>;
}
}
According to C# Specification 5.0
, there are two different kinds of conversion of as operator
.
If the compile-time type of
E
is notdynamic
, the operationE as T
produces the same result asE is T ? (T)(E) : (T)null
If the compile-time type of
E
isdynamic
, 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
Since, this is invalid because of (Foo<T>)str
str is Foo<T> ? (Foo<T>)str : (Foo<T>)null;
I thought it should be translated as:
str is Foo<T> ? (Foo<T>)(object)str : (Foo<T>)null;
But the spec says this only happens when the type of E
is dynamic
.
So my questions are:
E
is dynamic why first it casts E
to object
then T
while the (T)E
is completely valid?-= (Subtraction Assignment) – To store the difference of both the operands to the left side operand. *= (Multiplication Assignment) – To store the product of both the operands to the left side operand.
The -> is a separator. It is special symbol used to separate code with different purposes. It can be used to: Separate the parameters and body of a lambda expression val sum = { x: Int, y: Int -> x + y }
Binary operators require two operands. The arithmetic operators are the most familiar examples of binary operators.
The operators indicate what action or operation to perform. The operands indicate what items to apply the action to. An operand can be any of the following kinds of data items: Constant.
Is the compiler translating this expression to a code that is normally invalid?
After staring at the spec for about an hour, I'm starting to convince myself that this is simply an edge-case which was overlooked in the specification. Note that this is merely a way for the C# language composers to express the as
operator with the semantics of the is
operator.
The compiler doesn't actually convert the as
operator to a ternary operator with an is
. It will emit an IL call to isinst
, both for as
and is
:
IL_0000: nop
IL_0001: ldstr "foo"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: isinst class ConsoleApplication2.Foo`1<!T>
IL_000d: stloc.1
IL_000e: ret
Looking at the compiled DLL, the as
operator remains untouched.
When the type of E is dynamic why first it casts E to object then T while the (T)E is completely valid?
This is described in the fine-print of the specification:
If the compile-time type of E is dynamic, unlike the cast operator the as operator is not dynamically bound (§7.2.2). Therefore the expansion in this case is:
E is T ? (T)(object)(E) : (T)null
The cast to object
is needed to make the use of as
possible with dynamic
objects. as
is a compile-time operation while dynamic
objects are bound only at run-time.
The compiler actually treats dynamic
type objects as type object
to begin with:
class Foo<T>
{
public void SomeMethod()
{
dynamic str = "foo";
Foo<T> f = str as Foo<T>;
}
}
str
is actually treated as object
to begin with:
.class private auto ansi beforefieldinit Foo`1<T>
extends [mscorlib]System.Object
{
// Methods
.method public hidebysig
instance void SomeMethod () cil managed
{
// Method begins at RVA 0x2050
// Code size 15 (0xf)
.maxstack 1
.locals init (
[0] object,
[1] class Foo`1<!T>
)
IL_0000: nop
IL_0001: ldstr "foo"
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: isinst class Foo`1<!T>
IL_000d: stloc.1
IL_000e: ret
} // end of method Foo`1::SomeMethod
}
After talking to Vladimir Reshetnikov from the Managed Languages Team, he explains what the semantic of the representation from the "as operator" to "cast operator" actually tries to convay:
I agree, there is some imprecise language in the spec too. It says 'as' operator is always applicable if an open type involved, but then describes its evaluation in terms of casts, that might be not valid in some cases. It should say that casts in the expansion do not represent normal C# cast operator but just represent conversions that are permitted in 'as' operators. I'll take a note to fix it. Thanks!
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