http://blogs.clariusconsulting.net/kzu

Daniel Cazzulino's Blog

Go Back to
kzu′s Latest post

How to do reflection with a dynamic style

I’m not advocating *private* reflection (invoking private/internal fields, properties and methods), but we all know that there are those corner cases where you just can’t avoid it. And there’s a whole lot of scenarios when there are legitimate uses of reflection itself that can also leverage this very cool dynamic syntax (i.e. invoking generic methods where you don’t know the type at compile-time, etc.). In these cases, your code goes from pristine-looking C# to crappy unreadable reflection code.

David Ebbo has explained quite a bit already why this might be needed and how it can be used.

This is a pretty handy tool to have for those rare cases, and it’s pretty small, making it ideal for a NETFx NuGet little source-style package.

You install it either from the library package manager (search for “dynamic reflection”):

image

or the console (you can [TAB] for autocompletion right after “netfx-System.Dyn”).

And you get a new extension method (with source code, as usual) in the System.Dynamic namespace (didn’t want to put this in the global namespace as it would show up everywhere):

reflectiondynamic

Now you can do all sorts of interesting reflection-based code with nice clean C# syntax (even if it’s private, internal, static, or generics stuff). The implementation is fully covered by unit tests (yes all 100% of the functionality), and supports the following features:

  • Getting/setting properties, fields and indexed properties
  • Invoking methods with full overload resolution
  • Invoking methods with generic signature with C# generic signature or runtime Type as parameter
  • Invoking constructors: var instance = dynFoo.ctor(“hello”)
  • All of the above but also for static members, including static constructor: obj.cctor()
  • Both properties and methods can be explicitly implemented members (non-explicit has higher priority)
  • Support for out/ref parameters
  • Automatic recursive private access (i.e. foo.Bar.Baz, where the type of Bar is also a private class and Baz is a private member of it) for all supported invocations

 

Because this is just C# dynamic syntax, most examples (except for the constructors and out/ref) look just like plain C#, but keep in mind that they all are resolved at runtime using reflection:

var foo = Activator.CreateInstance("MyNamespace.Foo, MyAssembly");

// Get/set private field
int value = foo.AsReflectionDynamic().field1;
foo.AsReflectionDynamic().field1 = value++;

// Get/set property
int value = foo.AsReflectionDynamic().Id;
foo.AsReflectionDynamic().Id = value++;

string indexed = foo.AsReflectionDynamic().Items["key"];
foo.AsReflectionDynamic().Items["key"] = "hello";

// Recursively access a property in a nested private object
var value = foo.AsReflectionDynamic().Internals.InnerState.Id;

// Invoke method (does overload resolution)
var value = foo.Call("hello");

// Invoke method and recursively access result properties dynamically too
var value = foo.Call("hello").Internals.InnerState.Id;

// Invoke a method with a generic signature (does overload resolution too)
var value = foo.InternalServiceLocator.GetService<IFormatter>();

// Invoke private constructor passing values
var fooType = Type.GetType("MyNamespace.Foo, MyAssembly");
var foo = fooType.AsReflectionDynamic().ctor("Hello", 25);

// Invoke private (alternate?) constructor again after object has been initialized
var foo = Activator.CreateInstance("MyNamespace.Foo, MyAssembly");
foo.ctor("Hello", 25);

// Invoke static constructor
var fooType = Type.GetType("MyNamespace.Foo, MyAssembly");
fooType.AsReflectionDynamic.cctor();

 

Out/Ref require a little bit of explanation. There is a well known limitation in the underlying C# binder that does not copy out/ref values to the caller. This is a known bug that is supposedly fixed for .NET vNext. I managed to get something fairly usable IMHO to simulate out/ref. Essentially, the ReflectionDynamic knows about two special types or arguments you can pass to invocations: OutValue and RefValue. These are two generic classes with corresponding factory Create<T> methods that you pass delegates into for the get/set (Ref) or only set (Out). And the dynamic implementation will handily copy back and forth the values before/after invoking the actual member via Reflection:

// Invoke with output parameter
var service = default(IFormatter);
// Create a pass-through holder that can set the out value.
var out1 = OutValue.Create(value => service = value);

bool success = foo.InternalServiceLocator.TryGetService<IFormatter>(out1);
// if success == true, service1 will contain the output of the method call
// which has a signature of:
// bool TryGetService<TService>(out TService instance);

// Invoke with by-ref parameter
var service = default(IFormatter);
// Create a pass-through holder that can get/set the ref value.
var ref1 = RefValue.Create(() => service, value => service = value);

bool success = foo.InternalServiceLocator.TryGetService<IFormatter>(ref1);
// if success == true, service1 will contain the output of the method call
// which has a signature of:
// bool TryGetService<TService>(ref TService instance);

Another very useful but a little departed from C# functionality is to call generic methods but passing the generic type argument as a parameter (i.e. you need to call Resolve<T> but you only have a System.Type at runtime, back to ugly reflection land!):

var type = Type.GetType("Foo");
var instance = foo.InternalServiceLocator.Resolve(type.AsGenericParameter(), true);

// which has a signature of:
// TService Resolve<TService>(bool throwIfMissing);

You can even mix and match passing the generic parameters as traditional C# generics and as parameters to the invocation:

foo.InternalServiceLocator.Register<IFoo>(Type.GetType("Foo").AsGenericParameter(), "MyFoo");

// which has a signature of:
// void Register<TInterface, TService>(string contractName);

The types used as proper C# generics are always the first and next come the ones as parameters. i.e., if you wanted the TService to be IFoo, you’d have to pass both types as parameters.

This generics invocation is incredibly useful for general purpose APIs that rely a lot on generics for public consumption but which require general-purpose internal behavior. Now you can place the behavior in the generic members and invoke this way via reflection when someone uses the non-generic API, instead of the other way around which makes all code look substantially more convoluted.

Tests are a good place to learn about the various usage scenarios.

Go get it, and enjoy!

Comments

10 Comments

  1. You have two packages at NuGet, what should I use?

    And is this the same code that David Ebbo wrote?

  2. I am using version 1.0.0.22

    Can I send you a fix? At FindBestMatchImpl method, the 6th line:

    candidates = candidates.Where(x => x.Parameters[index].ParameterType == GetArgumentType(args[index]));

    Should be:

    candidates = candidates.Where(x => x.Parameters[index].ParameterType.IsAssignableFrom(GetArgumentType(args[index])));

  3. Fujiy: thanks for the feedback. I created a unit test for your fix but it doesn’t work, so I’ll leave it as it is for now. You might want to take a look at http://netfx.codeplex.com/documentation on how to contribute, fork the repo and send me a pull request :)

    Sorry for the messed up nuget repo. I cleared the old ones (I changed my mind on the name a couple times ;) ).

    Regarding David’s code: there’s not a single line that is common. His original one does about 20% of what this does (i.e. no statics, no ref/out, no generics, conversion required IConvertible, etc.)

    • kzu, I downloaded the code from Codeplex, did my code changes and all unit tests are working. I wrote this test too and worked:

      [TestMethod]
      public void WhenInvokingStaticMembersWithDerivedTypeArgument_ThenSucceeds()
      {
      var target = typeof(PrivateObject).AsDynamicReflection();
      target.cctor();

      List d = new List();

      List value = target.StaticMethod(d);

      Assert.Equal(d, value);
      }

      I will do what you say about fork when I come home

      Thanks

    • I forget to say, I created this method at PrivateObject

      public static IEnumerable StaticMethod(IEnumerable value)
      {
      return value;
      }

  4. I will test with unit tests. But my code just works with this change, because I am calling the method with a parameter of derived type

  5. Awesome Fujiy! You’ll become the first contributor to netfx ;)

  6. btw, would love your feedback on the end-to-end experience for contributing, as it’s quite key to this whole netfx thingy :)

  7. Have you looked into the performance difference between dynamicReflection and Relection?