Make reflection easier

Topics: APIs
May 15, 2014 at 2:11 AM
The reflection API/DSL is borderline incoherent.

Assuming you can navigate the api at all, you're then left with having to know what parts are ones that should be cached to not be eating up huge performance costs every time.

If only the last portion could be addressed that would be a substantial benefit.

The simplest change I could see is littering the methods that already have all kinds of parameters, with 1 more parameter. But an optional parameter. something along the lines bool willBeCalledManyTimes.

if this is true, the IL should be generated with appropriate (thread safe) caching included.
May 15, 2014 at 1:27 PM
This project is about the new C# (and VB) compiler and its API.

So, .Net libraries like reflection are off-topic here. I think your best bet is to post your idea to http://visualstudio.uservoice.com/.

Also, couldn't this be solved by a 3rd-party library of extension methods on the reflection types that add the caching you want?
Developer
Jul 13, 2014 at 5:07 AM
Also take a look at http://www.nuget.org/packages/Microsoft.Bcl.Metadata/1.0.11-alpha.
It's a low-level metadata reader. If the perf is critical for you this is the right tool. It certainly is very low-level though. We are working on adding more helper classes to it to address common scenarios.
Jul 13, 2014 at 4:55 PM
TomasMatousek wrote:
Also take a look at http://www.nuget.org/packages/Microsoft.Bcl.Metadata/1.0.11-alpha.
It's a low-level metadata reader. If the perf is critical for you this is the right tool. It certainly is very low-level though. We are working on adding more helper classes to it to address common scenarios.
Tomas, do you have any links to documentation or usage examples? The project site link on nuget doesn't return anything meaningful and my google-fu isn't turning up much either. Are there tests in the repo for this as examples? I can't seem to find the repo either.
Developer
Aug 13, 2014 at 5:51 AM
There are no official samples yet. There will be by the time we ship the reader. For now you can start with the following snippet:
using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection.Metadata;
using System.Reflection.PortableExecutable;

class Program
{
    static int Main(string[] args)
    {
        string path;
        if (args.Length == 0)
        {
            path = typeof(object).Assembly.Location;
        }
        else
        {
            path = args[0];
        }

        FileStream file;
        try
        {
            file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read);
        }
        catch (Exception e)
        {
            Console.WriteLine("Error reading {0}: {1}", path, e.Message);
            return 1;
        }

        try
        {
            using (PEReader peReader = new PEReader(file))
            {
                var mdReader = peReader.GetMetadataReader();
                PrintMethodSignatures(mdReader, "System.Collections.Generic", "Dictionary`2", ".ctor");
            }
        }
        catch (BadImageFormatException e)
        {
            Console.WriteLine("Invalid PE image format: {0}", e.Message);
            return 1;
        }

        return 0;
    }

    private static void PrintMethodSignatures(MetadataReader reader, string namespaceName, string typeName, string methodName)
    {
        TypeDefinition type;
        if (TryFindTypeDef(reader, namespaceName, typeName, out type))
        {
            foreach (var methodHandle in type.GetMethods())
            {
                var method = reader.GetMethod(methodHandle);
                if (reader.StringEquals(method.Name, methodName))
                {
                    var signature = reader.GetBytes(method.Signature);
                    Console.WriteLine(BitConverter.ToString(signature));
                }
            }
        }
    }

    private static bool TryFindTypeDef(MetadataReader reader, string namespaceName, string typeName, out TypeDefinition type)
    {
        foreach (var typeHandle in reader.TypeDefinitions)
        {
            type = reader.GetTypeDefinition(typeHandle);
            if (reader.StringEquals(type.Name, typeName) && reader.StringEquals(type.Namespace, namespaceName))
            {
                return true;
            }
        }

        type = default(TypeDefinition);
        return false;
    }
}