Skip to main content

Duck Typing with Anonymous Types - Part 3

Continuing my short series, it's time to add some polish to what we have so far.

Overload Resolution

At first I thought overload resolution was going to be a huge pain, but really the compiler does everything for us. As long as we have assigned an implementation with exactly matching parameters for the method that the compiler calls, we're good to go. The necessary modifications to the interceptor fit into a small reflection utility method.

private Delegate GetMethod(IInvocation invocation)
{
    var methodProperty = anonymous.GetPropertyValue(invocation.Method.Name);
    if (methodProperty is Delegate)
    {
        return methodProperty as Delegate;
    }

    var overloads = methodProperty as Delegate[];
    return overloads.Where(m => m.Method.SignatureMatches(invocation.Method)).First();
}
internal static bool SignatureMatches(this MethodInfo method, MethodInfo other)
{
    var theseParameters = method.GetParameters();
    var thoseParameters = other.GetParameters();

    if (theseParameters.Count() != thoseParameters.Count())
    {
        return false;
    }

    foreach (var parameter in theseParameters)
    {
        var parameterIndex = Array.IndexOf(theseParameters, parameter);
        if (!parameter.ParameterType.Equals(thoseParameters[parameterIndex].ParameterType))
        {
            return false;
        }
    }

    return true;
}

Here is the modified IDuck interface and corresponding test (note that by explicitly declaring brains as an object, the compiler will emit code that calls the overload with object parameter(s)):

public interface IDuck
{
    string Color { get; set; }
    Direction Fly();
    Direction Fly(Direction direction);
    string Quack();
    string Quack(int times);
    string Quack(string sound);
    string Quack(object sound);
    string Quack(params object[] sounds);
    object this[string attribute] { get; set; }
}
[Test]
public void CanInvokeOverloadedMethodsWithSomewhatAmbiguousSignatures()
{
    var zombie = new
    {
        Quack = new Delegate[]
        {
            (Func<int, string>)(i => String.Format("{0} BRAAAINS!", i)),
            (Func<string, string>)(s => String.Format("{0} BRAAAINS!", s)),
            (Func<object, string>)(o => String.Format("{0}", o)),
            (Func<object[], string>)(o => String.Format("{0} BRAAAIN OBJECTS!", o.Count()))
        }
    }.As<IDuck>();

    object brains = "BRAAAINS!";

    Assert.That("10 BRAAAINS!" == zombie.Quack(10));
    Assert.That("EAT BRAAAINS!" == zombie.Quack("EAT"));
    Assert.That("BRAAAINS!" == zombie.Quack(brains));
    Assert.That("3 BRAAAIN OBJECTS!" == zombie.Quack(brains, brains, brains));
}

Indexed Properties

I'm not going to go through all the changes I had to make to support an indexed property, but it wasn't very complicated. Basically, if we give our anonymous type a property named Item, it will correspond to an index on an interface (because an indexer is just syntactic sugar around an Item property). This property should be a Delegate[] with a method for the getter and/or setter (depending on the interface contract). The setter won't automatically store values unless we implement it that way. Here are some examples:

[Test]
public void CanInvokeIndexGetter()
{
    var buckshot = new object();

    var duck = new
    {
        Item = new Delegate[]
        {
            (Func<string, object>)(s => buckshot)
        }                     
    }.As<IDuck>();

    Assert.AreSame(buckshot, duck["weakness"]);
}

[Test]
public void CanInvokeIndexSetter()
{
    var isInfected = false;
    var duck = new
    {
        Item = new Delegate[]
        {
            (Action<string, object>)((key,value) => isInfected = true)               
        }   
    }.As<IDuck>();

    duck["disease"] = "bird flu";
    Assert.IsTrue(isInfected);
}

[Test]
public void CanStoreAndRetrieveWithIndexedProperty()
{
    var store = new Dictionary<string, object>();
    var duck = new
    {
        Item = new Delegate[]
        {
            (Func<string, object>)(s => store[s]),
            (Action<string, object>)((s,o) => store[s] = o)
        }
    }.As<IDuck>();

    var feathers = new object();
    duck["feathers"] = feathers;
            
    Assert.AreSame(feathers, duck["feathers"]);
}

Duck Typing Plain Objects

A few fairly simple modifications, and we can support duck typing objects that aren't anonymous types.

public class ScroogeMcDuck
{
    public string Quack()
    {
        return "Bah! Humbug!";
    }
}

[Test]
public void CanInvokeMethod()
{
    var scrooge = new ScroogeMcDuck().As<IDuck>();

    Assert.That("Bah! Humbug!" == scrooge.Quack());
}

Syntax

Casting the function/action type of the method can look kind of ugly, so I made a utility class that we can pass anonymous methods through to return Delegates. We still have to specify parameter types, so it's still pretty crusty, but it's there if we want to use it.

var duck = new
{
    Quack = Anon.Methods
    (
        Anon.Method(() => "Quack!"),
        Anon.Method<int, string>(i => String.Format("{0} quacks!", i))
    )
};

Conclusion

This is a somewhat serviceable solution for ducktyping adaptation of legacy code or quick mocking, especially of data. We haven't made any special considerations for generics, so it may fail in those scenarios. Named parameters will almost certainly cause problems, and we haven't done any performance tuning. I should also note that I used this post by Mauricio Scheffer as a resource when researching how to begin. The code can be found here on github.

Comments

Popular posts from this blog

Who I'm Is

I am a junior .NET developer currently working in Chicago, IL. I am starting this blog in order to enhance my knowledge of programming subject matter. Hopefully, someone else will be helped along the way. This first post will probably be edited soon...

Stubbing Static Methods with PostSharp

TypeMock uses the Profiler API to allow mocking, stubbing, etc. of classes used by code under test. It has the ability to handle sealed classes, static classes, non-virtual methods, and other troublesome-yet-oft-encountered scenarios in the world of unit testing. Other frameworks rely on proxies to intercept method calls, limiting them to be able to only fake virtual, abstract, and interface members. They also rely on dependecy injection to place the proxies as the concrete implementation of calls to the abstracted interface members. Anyone working with a legacy codebase is bound to run into static method calls (especially in the data access layer), dependencies on concrete types with non-virtual methods, and sealed class dependencies (HttpContext anyone?). The only way to unit test this without refactoring is with TypeMock. I've never used TypeMock, and I'm sure it's a great product, but it's not free. I decided to spike some code to see if I could solve the prob...

Strongly-Typed Property Constraints in Rhino Mocks

UPDATE: As I suspected, this functionality was right in front of my face – it’s called PredicateConstraint in Rhino Mocks. I also realize that I managed to completely ignore the existence of Predicate<T> in the framework, and write my own predicate delegate. Hey, I was on a roll. Rhino Mocks has a PropertyConstraint class that allows you to check the values on properties of objects passed into the method as part of the verification for that method being called. Unfortunately, the name of the property is specified as a string, which means the benefits of strong-typing that Rhino Mocks is normally so good at preserving are lost. Here’s an example (using Rhino Mocks 3.3 and .NET 2.0): [Test] public void Main_Form_Should_Show_Start_Panel_On_Load() { MockRepository mockRepository = new MockRepository(); IMainFormView mockView = mockRepository.DynamicMock<IMainFormView>(); IEventRaiser loadEventRaiser = GetEventRaiserFor(delegate { mockView.Load += null; });...