parameterof, propertyof, methodof 8

Posted by Jb Evain Wed, 05 May 2010 15:09:00 GMT

Every C# programmer knows about the keyword typeof, in essence, it returns an instance of System.Type for a given type. It’s used everywhere you have to do reflection on a type known at compilation time. You could achieve pretty much the same with the methods GetType(string) of both Module and Assembly, but the nice thing about typeof is that it’s refactoring proof.

Now, every C# programmer also knows that typeof is the only keyword with such abilities. There’s no parameterof, propertyof, methodof, or eventof to access other metadata members. The web is filled with ways to emulate a type safe reflection though. Lately, expression trees helped a lot. This post presents a proof of concept piece of code for a different approach.

Considering the following types:

public static class Parameter {

    public static ParameterInfo Of<T> (T value)
    {
        return null;
    }
}

public static class Property {

    public static PropertyInfo Of<T> (T value)
    {
        return null;
    }

    public static PropertyInfo Of (MethodInfo method)
    {
        const BindingFlags all = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance;

        foreach (var property in method.DeclaringType.GetProperties (all))
            if (method.Equals (property.GetGetMethod (true)))
                return property;

        return null;
    }
}

public static class Method {

    public static MethodInfo Of<T> (Func<T> value)
    {
        return null;
    }

    public static MethodInfo Of<T1, TRet> (Func<T1, TRet> value)
    {
        return null;
    }

    public static MethodInfo Of (Action value)
    {
        return null;
    }

    public static MethodInfo Of<T> (Action<T> value)
    {
        return null;
    }
}

They are basically just stubs, but let’s use them anyway:

class Program {

    static void Main (string [] args)
    {
        ParameterInfo parameter = Parameter.Of (args);

        Console.WriteLine ("Parameter of args: {0}", parameter);

        PropertyInfo rank = Property.Of (args.Rank);

        Console.WriteLine ("Property of rank: {0}", rank);

        MethodInfo foo = Method.Of<int> (Foo);

        Console.WriteLine ("Foo method: {0}", foo);

        MethodInfo bar = Method.Of<int> (Bar);

        Console.WriteLine ("Bar method: {0}", bar);
    }

    static int Foo ()
    {
        return 42;
    }

    static void Bar (int b)
    {
        return;
    }
}

Of course if you run the code as is, everything will be null. What I did is that I wrote a little Cecil rewriter that will detect the calls to the different Of methods, and rewrite them to something that will give you the expected results.

So for instance, the Parameter.Of call is compiled as:

ldarg.0
call class [mscorlib]System.Reflection.ParameterInfo Parameter::Of<string[]>(!!0)

But is rewritten in two parts. First we emit at the beginning of the method a way to retrieve of the ParameterInfo of the current method:

ldtoken method void Program::Main(string[])
call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
callvirt instance class [mscorlib]System.Reflection.ParameterInfo[] [mscorlib]System.Reflection.MethodBase::GetParameters()
stloc $reflection_parameters

And we simple replace the Of call with a load from the $reflection_parameters array:

ldloc $reflection_parameters
ldc.i4 0
ldelem.ref

For methods it’s a bit different, they are compiled as:

ldnull
ldftn int32 Program::Foo()
newobj instance void class [mscorlib]System.Func`1<int32>::.ctor(object,native int)
call class [mscorlib]System.Reflection.MethodInfo Method::Of<int32>(class [mscorlib]System.Func`1<!!0>)

And we rewrite them to remove the creation of a delegate:

ldtoken method int32 Program::Foo()
call class [mscorlib]System.Reflection.MethodBase [mscorlib]System.Reflection.MethodBase::GetMethodFromHandle(valuetype [mscorlib]System.RuntimeMethodHandle)
castclass  [mscorlib]System.Reflection.MethodInfo

For properties, it’s exactly the same, but we have to inject a call to a method that will retrieve the PropertyInfo the get method belongs to.

If you’re interested you can read the rewriter code. It’s pretty simple, but is nowhere complete, and I doubt anyone would use it anyway, but it’s still a pretty cool hack.

Trackbacks

Use the following link to trackback from your own site:
http://evain.net/blog/articles/trackback/683

  1. Interesting Finds: May 6, 2010
Comments

Leave a response

  1. Avatar
    Scott Wed, 05 May 2010 17:11:05 GMT

    (As I’m sure you know) typeof actually turns into a ldtoken and then a call System.Type::GetTypeFromHandle. The ldtoken instruction actually works with methods, fields, and arguments as well, but there are no corresponding GetMethodInfoFromHandle &c methods. This is a huge oversight in the API if you ask me. It would allow for languages (or il-rewriter-based APIs such as this) to increase performance of these operations.

  2. Avatar
    Jb Evain Wed, 05 May 2010 17:15:33 GMT

    @Scott,

    Indeed, typeof turns into a ldtoken. And my rewrite turns methods into ldtoken as well. And you’re wrong, there’s a GetMethodFromHandle, that’s exactly what the rewriter is using. You should have a look at it.

    Also, ldtoken only takes tokens for types, methods and fields.

    So everything needed is in place. It’s just that the C# language lacks this feature.

    We could implement it in mcs for fun too.

  3. Avatar
    Scott Wed, 05 May 2010 19:39:25 GMT

    ACK! How did I miss this?! Well then, carry on. Nothing to see here.

  4. Avatar
    Scott Wed, 05 May 2010 19:41:33 GMT

    Speaking of language support, I think a host of new keywords (parameterof, propertyof, &c.) would be painful. How about a simple reflect() operator. There was a proposal for this here: http://cheddar.wik.is/%22reflect%22_Operator

  5. Avatar
    Jb Evain Wed, 05 May 2010 19:50:48 GMT

    For some subjectives reasons, I quite don’t like `reflect’.

  6. Avatar
    Scott Wed, 05 May 2010 20:06:31 GMT

    typeof is perhaps my least favorite C# keywords (it is two words, the casing is out of style with the Framework Design Guidelines for identifiers). Maybe we could come up with a new syntax which is a superset of C# 4. That way it would be 100% backward compatible. Square braces?

    [string] [Type] [Type.ToString()]

    I don’t think there is any danger of ambiguity. This shouldn’t overlap with the indexer syntax.

  7. Avatar
    Pascal Thu, 06 May 2010 11:41:19 GMT

    What about “eventof” ? :) or more generic “infoof” ?

  8. Avatar
    Jb Evain Thu, 06 May 2010 12:58:27 GMT

    It should be very easy to add fieldof and eventof in the rewriter. Actually I just did.

Comments