Notes from Daily Encounters with Technology RSS 2.0
 
# Sunday, July 24, 2011

Reflection is a great tool for calling methods on objects when their types are not known at compile time. Unfortunately the Type.GetMethod method doesn’t work with generic methods therefore we seem to be stuck with finding the right method by iterating through all the methods returned by Type.GetMethods:

public static MethodInfo GetMethodWithLinq(this Type staticType, string methodName, params Type[] paramTypes)
{
    var methods = from method in staticType.GetMethods()
                  where method.Name == methodName
                        && method.GetParameters()
                                  .Select(parameter => parameter.ParameterType)
                                  .Select(type => type.IsGenericType ? type.GetGenericTypeDefinition() : type)
                                  .SequenceEqual(paramTypes)
                  select method;
    try
    {
        return methods.SingleOrDefault();
    }
    catch (InvalidOperationException)
    {
        throw new AmbiguousMatchException();
    }
}

Searching for alternatives I stumbled upon a solution using expression trees posted by Neil Whitaker on StackOverflow:

private static MethodInfo GetMethodWithExpressions(Type innerType)
{
    var method = typeof(Program).GetMethod("GetSingleOrDefaultMethod", 
                                           BindingFlags.Static | BindingFlags.NonPublic, 
                                           null, Type.EmptyTypes, null)
                                .MakeGenericMethod(innerType);
    return method.Invoke(null, new object[] { }) as MethodInfo;
}

private static MethodInfo GetSingleOrDefaultMethod<TSource>()
{
    Expression<Func<TSource, bool>> fakePredicate = i => true;
    Expression<Func<IQueryable<TSource>, TSource>> lambda = l => l.SingleOrDefault(fakePredicate);

    return (lambda.Body as MethodCallExpression).Method;
}

Sure, it is more difficult to comprehend and a lot less flexible, requiring a helper method building the expression tree for every method to be returned as MethodInfo. It got me wondering though, which of the two solutions actually performs better. Let’s try them out:

static void Main(string[] args)
{
    MethodInfo method;
    Type innerType = typeof(string);

    int count = 10000;
    Stopwatch stopwatch = new Stopwatch();

    stopwatch.Restart();
    for (int i = 0; i < count; i++)
    {
        method = typeof(Queryable).GetMethodWithLinq("SingleOrDefault", typeof(IQueryable<>), typeof(Expression<>))
                                  .MakeGenericMethod(innerType);
    }
    stopwatch.Stop();
    Console.WriteLine(String.Format("Method 1: {0}", stopwatch.ElapsedMilliseconds));

    stopwatch.Restart();
    for (int i = 0; i < count; i++)
    {
        method = GetMethodWithExpressions(innerType);
    }
    stopwatch.Stop();
    Console.WriteLine(String.Format("Method 2: {0}", stopwatch.ElapsedMilliseconds));

    Console.ReadLine();
}

And the results?

Method 1: 224
Method 2: 396

Not only is the Type.GetMethods based approach simpler, it also consistently performs a lot better. We’ll just stick with it until support for generic methods is added to Type.GetMethod.

Sunday, July 24, 2011 10:31:43 AM (Central European Daylight Time, UTC+02:00)  #    Comments [0] - Trackback
Development | .NET | C#
# Sunday, November 28, 2010

While reading an article on the difference between const and readonly it surprised me that changes to public consts in the referenced assembly don’t affect the referencing assembly unless it is recompiled using the changed referenced assembly. The C# Reference doesn’t hint at such behavior at all, which means it’s time for further exploration.

A sample can be pretty straight forward. Let’s start with a single class in the library:

public class Urls
{
    public const string ProductWebPage = "http://www.damirscorner.com/myproduct";
}

The reference the library in a test application:

class Program
{
    static void Main(string[] args)
    {
        Console.WriteLine("Product URL: " + Urls.ProductWebPage);
        Console.ReadKey();
    }
}

Compiling both assemblies and running the application returns the expected result. The surprise comes if the value in the library changes and the application doesn’t get recompiled. The new value in the library could be:

public class Urls
{
    public const string ProductWebPage = "http://www.myproduct.com";
}

The application will return the new value only if it is also recompiled. As long as only the changed referenced assembly is copied to the application folder, the old URL value will still be displayed. Checking the compiled MSIL, this behavior can easily be explained:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       19 (0x13)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "Product URL: http://www.myproduct.com"
  IL_0006:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_000b:  nop
  IL_000c:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_0011:  pop
  IL_0012:  ret
} // end of method Program::Main

The constant value is not referenced at all. It is included into the referencing assembly as a literal. Consequently the compiled application doesn’t reference the library in its manifest at all and will still work even if the library is deleted from the application folder:

// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly MyProduct
{
  // ...

To avoid the problem, public constant values in libraries should always use static readonly modifiers instead of const:

public class Urls
{
    public static readonly string ProductWebPage = "http://www.damirscorner.com/myproduct";
}

The application code remains the same; the compiled MSIL is different:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       29 (0x1d)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "Product URL: "
  IL_0006:  ldsfld     string [Library]DamirsCorner.Samples.Const.Urls::ProductWebPage
  IL_000b:  call       string [mscorlib]System.String::Concat(string,
                                                              string)
  IL_0010:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_0015:  nop
  IL_0016:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
  IL_001b:  pop
  IL_001c:  ret
} // end of method Program::Main

Now the changes in the library will reflect in the application even without recompiling it. Of course the application also references the library in its manifest and won’t work without it:

// Metadata version: v4.0.30319
.assembly extern mscorlib
{
  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
  .ver 4:0:0:0
}
.assembly extern Library
{
  .ver 1:0:0:0
}
.assembly MyProduct
{
  // ...

The moral of the story: “Know Thy Language”. And never use const for public constant values in libraries.

Sunday, November 28, 2010 11:40:37 AM (Central European Standard Time, UTC+01:00)  #    Comments [0] - Trackback
Development | .NET | C#
Page 1 of 1 in the DevelopmentC category
Sponsored Ads

About Me
Twitter
@MladenPrajdic @andrejt use the middle mouse button then 2 days ago
Great #DotNetRocks show: Troy Hunt Secures http://t.co/oxClbXLe http://t.co/MiMasNuZ PDF is worth checking out as well http://t.co/z4BHAzqh 3 days ago
Hazards of Converting Binary Data To A String http://t.co/lb8kRSsU via @haacked 5 days ago
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

All Content © 2012, Damir Arh, M. Sc. Send mail to the author(s) - Privacy Policy - Sign In
Based on DasBlog theme 'Business' created by Christoph De Baene (delarou)
Social Network Icon Pack by Komodo Media, Rogie King is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.