Generic Types are prettier than Dictionaries

If you need to pass key-value-pairs to a method you can find many modern implementations prefer to take them as an “object” rather than a dictionary. This allows you to create a anonymous object which is prettier than a dictionary: It is a shorter notation, and it brings along some of the advantages of static typing:

var result = MyMethodThatTakesAnAnonymousObject(
    new { foo = "bar", baz = 1 };

 

The method then reflects on the object and uses the property names as keys and their values as the – values.

What you might not have been aware of: The same can be done for return objects. If the caller of the method knows what keys it expects, you can support it by taking this expectation as an anonymous object:

var result = PleaseReturnTheResultInAnObjectLikeThis(
    new { foo = "", bar = 0 });
Console.WriteLine(result.foo);

 

And this is how it works:

[TestClass]
public class LittleTest
{
    [TestMethod]
    public void Test_A_Little()
    {
        var t = PleaseReturnTheResultInAnObjectLikeThis(new { Foo = "" });
        Assert.AreEqual("hello", t.Foo);
    }
    private T PleaseReturnTheResultInAnObjectLikeThis<T>(T input)
    {
        // The following line simulates any logic that creates keys and
        // values in any form. You can use LINQ to transform them into
        // a dictionary as a pre-step to fill them into your T object.
        var values = new Dictionary<string, object> { { "Foo", "hello" } };

        // The next line brings us to the heart of the idea
        return MapToGenericType(input, values);
    }
    private static T MapToGenericType<T>(T input, IDictionary<string, object> values)
    {
        var constructorInfo = input.GetType().GetConstructors().Single();
        var parameterInfos = constructorInfo.GetParameters().ToArray();
        var parameters = new object[parameterInfos.Length];
        foreach (var parameterInfo in parameterInfos)
        {
            parameters[parameterInfo.Position] = values[parameterInfo.Name];
        }
        var output = (T) constructorInfo.Invoke(parameters);
        return output;
    }
}

The clue is that an anonymous object always brings along its anonymous type with a constructor whose parameters are 1:1 the properties that you defined for the anonymous object. You can use generics to transport knowledge of the type of any anonymous object beyond the scope where it was defined.

This way you kind of define an expectation to the results in a static-typed manner. Your compiler will not guarantee that the expectations hold true though, you will only find it by the time that MapToGenericType is executed.

Advertisements