Skip to content

Instantly share code, notes, and snippets.

@AArnott
Created August 21, 2014 21:21
Show Gist options
  • Save AArnott/d285feef75c18f6ecd2b to your computer and use it in GitHub Desktop.
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.
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();
}
}
}
.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
@nguerrera
Copy link

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