Thursday, February 21, 2013

dynamic method calls in C#

Here is some more dynamic magic from C# 4.0 in a Nutshell. Consider these four classes:

  1. using System.Dynamic;
    namespace DynamicGunk.Objects
    {
       public class TourettesPatient : DynamicObject
       {
          public string Scream()
          {
             return "Aaaaaaaaaahhhhhhhhhhhhh!!!!!!!";
          }
       }
    }
     
  2. namespace DynamicGunk.Objects
    {
       public class EncephalitisLethargicPatient
       {
          public string SayNothing()
          {
             return "";
          }
       }
    }
     
  3. namespace DynamicGunk.Objects
    {
       public class IceCreamVan
       {
          public IceCreamCone GiveIceCreamToThoseWhoScream(dynamic person)
          {
             string vocalization = person.Scream();
             if (vocalization == "Aaaaaaaaaahhhhhhhhhhhhh!!!!!!!")
             {
                return new IceCreamCone();
             } else {
                return null;
             }
          }
       }
    }
     
  4. namespace DynamicGunk.Objects
    {
       public class IceCreamCone
       {
       }
    }
     

 
 

Here is the magic. This test passes! We may call the Scream method from within IceCreamVan as shown above if we are handing in a TourettesPatient.

using DynamicGunk.Objects;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace DynamicGunk.Tests
{
   [TestClass]
   public class UnitTest
   {
      [TestMethod]
      public void Test()
      {
         TourettesPatient jack = new TourettesPatient();
         EncephalitisLethargicPatient jill = new EncephalitisLethargicPatient();
         IceCreamVan van = new IceCreamVan();
         IceCreamCone treat = van.GiveIceCreamToThoseWhoScream(jack);
         Assert.AreNotEqual(treat, null);
      }
   }
}

 
 

If we hand jill to van instead of jack, we do not get a failing test however, we end up with a test that throws an exception (as there is no Scream method on EncephalitisLethargicPatient to call). We have to alter IceCreamVan to make the jill scenario not blow up on us.

namespace DynamicGunk.Objects
{
   public class IceCreamVan
   {
      public IceCreamCone GiveIceCreamToThoseWhoScream(dynamic person)
      {
         try
         {
            string vocalization = person.Scream();
            if (vocalization == "Aaaaaaaaaahhhhhhhhhhhhh!!!!!!!")
            {
               return new IceCreamCone();
            }
         }
         catch
         {
         }
         return null;
      }
   }
}

 
 

This test will fail.

using DynamicGunk.Objects;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace DynamicGunk.Tests
{
   [TestClass]
   public class UnitTest
   {
      [TestMethod]
      public void Test()
      {
         TourettesPatient jack = new TourettesPatient();
         EncephalitisLethargicPatient jill = new EncephalitisLethargicPatient();
         IceCreamVan van = new IceCreamVan();
         IceCreamCone treat = van.GiveIceCreamToThoseWhoScream(jill);
         Assert.AreNotEqual(treat, null);
      }
   }
}

 
 

The sort of exception swallowing we used here to make the two very different types that GiveIceCreamToThoseWhoScream may consume both manageable is pretty dirty. I am still trying to figure out the best sort of pattern for this sort of thing.

No comments:

Post a Comment