Fun with C#: After dynamic features arrived

Today I’ll continue with our series on dynamic stuff, taking it from where we left off in our previous post. So, building on top of the same simple examples, let’s see what changed for me once the dynamic features arrived to C#.

Bending strong typing

Remember how C# is strongly-typed?

var myVariable = 1234;
myVariable = “Woohoo!!”;   // Nope...

We can bend that rule if we declare types as dynamic:

dynamic myVariable = 1234;
myVariable = “Oh, boy…”;

Yes, I hope you can see how writing code like that can cause all sorts of trouble. But, it does come in handy when we have good use for it. Anyway, when we declare something as dynamic, we’re telling the C# compiler “look, the actual type of this will have to be determined during runtime”. Of course, in the sample above, we have an integer and string, so it wouldn’t make sense at all to use dynamic there. However, in real world apps, the actual value types would only be known during runtime.

Now, take this example:

var names = new[] { "Claudio", “Paul" };
var result = names.Select(n => new { Length = n.Length }).ToList();
Console.WriteLine(result.GetType().Name); // List’1 

Notice that the type of result is List’1, because we’re projecting the items into new anonymous types, and using var to let the compiler infer the type.

So what happens if we changed the code to this:

dynamic result = names.Select(n => new { Length = n.Length }).ToList();
Console.WriteLine(result.GetType().Name);  // List’1

By changing the type of the variable to dynamic we told C# the type would only be known during runtime. Regardless, in that case, the type still ends up being List’1 at runtime.

What if we follow that code with this:

result = “Woohoo";
Console.WriteLine(result.GetType().Name);  // string

The type of result would be changed to string, which proves the data type is indeed dynamic.

The ever expanding object

As part of the dynamic features, we also got an ExpandoObject class. If we instantiate that class and type the variable to either ExpandoObject or var, we get access to the members of that class that were statically typed. But the real fun comes in when we type the variable to dynamic. Check this out:

dynamic user = new ExpandoObject();            

user.FirstName = "Claudio”;            
user.LastName = "Lassala”;            

user.FullName = new Func(() => $"{user.FirstName} {user.LastName}");            

Console.WriteLine(user.FullName()); 

In the example above, the FirstName and LastName are properties we added dynamically to that user object. FullName is a method that returns a string.

We could also have defined the FullName method before defining the other properties:

dynamic user = new ExpandoObject();         

user.FullName = new Func(() => {0}#x22;{user.FirstName} {user.LastName}");

user.FirstName = "Claudio”;            
user.LastName = "Lassala”;             

Console.WriteLine(user.FullName()); 

That would still work just fine, given that during runtime, when FullName() was called for the first time, the properties it depends on would already have been added to the object. What if the properties weren’t there already? It’d throw an exception, which we can handle gracefully.

Let’s revisit a sample from previous posts and make it use dynamic:

var instanceType = Type.GetType("Playground.User");            
dynamic instance = Activator.CreateInstance(instanceType);            
object hello = instance.SayHello();            
Console.WriteLine(hello.ToString());

The main change there is that we assign the return of CreateInstance to a dynamic variable. From there on, we can call methods on it that the compiler doesn’t know about during compile time. NOTE: this feature was great when working with Excel automation!!!

Now that we know all of that, going back to the idea that I wanted to instantiate classes and call methods for which I have strings that represent their names (these strings are likely coming from a database or configuration file, for instance). So I’d like to be able to write something like this:

dynamic instance = MakeObject("Playground.User");            
dynamic result = instance.Call("SayHello");            
Console.WriteLine(result);

That’s an easy thing to pull off now that we know about ExpandoObject and dynamic types. Here’s the implementation of MakeObject:

private static dynamic MakeObject(string className)
{    
    var instanceType = Type.GetType(className);    
    dynamic instance = Activator.CreateInstance(instanceType);    

    dynamic expando = new ExpandoObject();    

    expando.Call = new Func((methodName) =>    
    {        
        IEnumerable methods = instance.GetType().GetMethods();        
        var method = methods.SingleOrDefault(x => x.Name == methodName);        
        return method.Invoke(instance, null);    
    });                
    return expando;
}

So we use Reflection to instantiate the given type, add a Call method to an ExpandoObject, which in turn calls the method we want on the real object, also using Reflection, and things just work!

Using Reflection in this case is just one option. If this approach presents a performance issue, the Reflection part can be replaced with other alternatives (IL emit, dynamic compilation of code, building Expression Trees), but the client code of this functionality wouldn’t have to change: it’d still be using strings to know the name of the class to instantiate and the name of the method to call on the object.

This series will continue, as there’s quite a number of some other useful things we can do with this knowledge acquired so far!

  1. #1 by Ant. Rui Nogueira on May 2, 2017 - 11:41 am

    In C# we have to use a lot of code. Coding is no longer so fun.

    • #2 by claudiolassala on May 2, 2017 - 12:05 pm

      🙂 In the last 6 years I’ve liked Ruby better, but I can still appreciate some C#.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: