I found the "optional parameters" feature in C# 4.0 very interesting, so I tried to figure out how they made it happen. so I wrote a method like this:
private static void A(int a = 5) { }
Compiled it, then decompiled it in IL DASM, this is the IL code:
.method private hidebysig static void A([opt] int32 a) cil managed
{
.param [1] = int32(0x00000005)
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
} // end of method Program::A
And it has got this in its metadata:
(1) ParamToken : (08000002) Name : a flags: [Optional] [HasDefault] (00001010) Default: (I4) 5
So I followed the clue and wrote a method like this:
private static void B([Optional, DefaultParameterValue(78)]int b) { }
Compiled it and decompiled it, and I found that the C# compiler generated almost the identical MSIL code for method A and B(except for the name).
As we can see there is no sign of attributes in the IL code and it felt wrong, so I wrote a custom attribute like this:
[AttributeUsage(AttributeTargets.Parameter)]
public class MyTestAttribute : Attribute
{
}
Then used it in method C like this:
private static void C([MyTest]int c) { }
Compiled it and then decompiled it, and hah, I found this:
.method private hidebysig static void C(int32 c) cil managed
{
.param [1]
.custom instance void ConsoleApplication1.MyTestAttribute::.ctor() = ( 01 00 00 00 )
// Code size 2 (0x2)
.maxstack 8
IL_0000: nop
IL_0001: ret
} // end of method Program::C
The second line of the method body calls to the ctor of my custom attribute.
So this leads to my doubts:
Thanks in advance.
The C# compiler doesn't need to emit the attributes since the Param metadata table can already describe optional and default values via the Flags
column.
From 23.1.13 in ECMA 335:
Flag Value Description
-----------------------------------------------------
In 0x0001 Parameter is [In]
Out 0x0002 Parameter is [Out]
Optional 0x0010 Parameter is optional
HasDefault 0x1000 Parameter has a default value
HasFieldMarshal 0x2000 Parameter has FieldMarshal
A parameter can have a flag value that specifies it is optional and has a default value (0x0010 | 0x1000). Parameters that have a default value will have an associated token in the Constant metadata table.
The Constant metadata table has a Parent
column that would be the Param token in question and a Value
column that would be an index into the blob heap where the default value is stored.
So to answer your questions:
Flags
column for the Param token has the Optional flag set.2 / 3; there are a few attributes that the compiler interprets as IL metadata, not really attributes; it looks like this is the case here; [Serializable]
is another example. The data for the default is there: Default: (I4) 5
- not all attributes in code become attributes in the metadata (again, I'm looking at [Serializable]
here)
re that point on [Serializable]
(comments); here's an example:
[Description("abc")]
class Foo { }
[Serializable]
class Bar { }
for which the core IL is:
.class private auto ansi beforefieldinit Foo
extends [mscorlib]System.Object
{
.custom instance void [System]System.ComponentModel.DescriptionAttribute::.ctor(string) = { string('abc') }
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
}
}
.class private auto ansi serializable beforefieldinit Bar
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
}
}
In Foo
(for some arbitrary attribute), we get:
.custom instance void [System]System.ComponentModel.DescriptionAttribute::.ctor(string) = { string('abc') }
However this does not apply for [Serializable]
; instead, that is part of the type:
.class private auto ansi serializable beforefieldinit Bar
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