Created
August 21, 2014 21:21
-
-
Save AArnott/d285feef75c18f6ecd2b to your computer and use it in GitHub Desktop.
Creating a static method that accepts a first argument supplied by the delegate.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
namespace ILExaminer | |
{ | |
using System; | |
static class Program | |
{ | |
internal static Func<T> AsFunc<T>(this T value) | |
where T : class | |
{ | |
return new Func<T>(value.Return); | |
} | |
private static T Return<T>(this T value) | |
{ | |
return value; | |
} | |
static void Main(string[] args) | |
{ | |
Func<string> foo = "hi".AsFunc(); | |
string v = foo(); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
.class private abstract auto ansi sealed beforefieldinit ILExaminer.Program | |
extends [mscorlib]System.Object | |
{ | |
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) | |
.method assembly hidebysig static class [mscorlib]System.Func`1<!!T> | |
AsFunc<class T>(!!T 'value') cil managed | |
{ | |
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) | |
// Code size 18 (0x12) | |
.maxstack 8 | |
IL_0000: ldarg.0 | |
IL_0001: box !!T | |
IL_0006: ldftn !!0 ILExaminer.Program::Return<!!0>(!!0) | |
IL_000c: newobj instance void class [mscorlib]System.Func`1<!!T>::.ctor(object, | |
native int) | |
IL_0011: ret | |
} // end of method Program::AsFunc | |
.method private hidebysig static !!T Return<T>(!!T 'value') cil managed | |
{ | |
.custom instance void [mscorlib]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) | |
// Code size 2 (0x2) | |
.maxstack 8 | |
IL_0000: ldarg.0 | |
IL_0001: ret | |
} // end of method Program::Return | |
.method private hidebysig static void Main(string[] args) cil managed | |
{ | |
.entrypoint | |
// Code size 19 (0x13) | |
.maxstack 1 | |
.locals init ([0] class [mscorlib]System.Func`1<string> foo) | |
IL_0000: ldstr "hi" | |
IL_0005: call class [mscorlib]System.Func`1<!!0> ILExaminer.Program::AsFunc<string>(!!0) | |
IL_000a: stloc.0 | |
IL_000b: ldloc.0 | |
IL_000c: callvirt instance !0 class [mscorlib]System.Func`1<string>::Invoke() | |
IL_0011: pop | |
IL_0012: ret | |
} // end of method Program::Main | |
} // end of class ILExaminer.Program | |
OK, I figured out why this box must be emitted even if T is constrained to be a reference type. It is required to be verifiable. The key is in the next sentence in the spec that I left out of the above excerpt:
However the type tracked by verification is always “boxed” typeTok for generic
parameters, regardless of whether the actual type at runtime is a value or reference type.
If you strip out that box via ildasm/ilasm and run peverify, you get something like:
[IL]: Error: [C:\Program.exe : Program::Foo[T]]
[offset 0x00000002]
[found (unboxed) 'T']
[expected ref 'System.Object']
Unexpected type on the stack.
So from the verifier's perspective, you simply can't pass an 'unboxed' T to object. It does not take in to account the constraint and that makes sense when you factor in that requiring the box has no impact on the native code that will run.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
As discussed on Twitter this is will be a no-op and it's not a bug.
Relevant excerpt from CLI spec III.4.1:
I suspect there's no point in special casing the class constraint in the compiler when it would merely save a no-op in IL.