Monday 13 February 2012

Downcasting Using Dynamics- C#

A few days ago I learned something new. 

For certain unfortunate reasons sometimes one is forced to downcast an object before sending it to a method which takes a generic parameter. Again, this isn't an ideal case as it seems less generic, but just in case someone else has found themselves in such a pickle because of other constraints, I hope you find this solution as helpful as I have. In C# 4.0 the dynamic was introduced.



Given a couple of tools that inherit from a tool object:



public abstract class Tool {
}
public class Wrench : Tool {
}
public class ScrewDriver : Tool {
}

And a method that takes the generic:


public class ToolHandler {
    public virtual void Tighten<T>(T tool) {
    }
}


How does one downcast the object from a list before calling the method without checking each type in a list?


public List<Tool> Tools { get; set; }

var _toolHandler = new ToolHandler();
List<Tool> tools = new List<Tool>{new Wrench(), new ScrewDriver()};



*************Former sloppy way******************

foreach(var tool in tools){
    if (tool is Wrench) {
        _toolHandler.Tighten((Wrench)tool);
    }
    if (tool is ScrewDriver) {
        _toolHandler.Tighten((ScrewDriver)tool);
    }
}    

****************************************************

The answer- use a dynamic. 

foreach(var tool in tools){
    _toolHandler.Tighten((dynamic)tool);
}


*Special thanks to M.Babcock on stackoverflow for answering my co-worker's and my question. http://stackoverflow.com/questions/9116034/downcast-instance-using-its-type

Feel free to paste this inside Rextester (http://rextester.com/runcode) or wherever you would like to run it for an example. Hopefully the link still works too http://rextester.com/live/IJWT40081.



using System;
using System.Collections.Generic;

namespace Rextester
{
    public class Program
    {
        public abstract class Tool {
        }
        public class Wrench : Tool {
        }
        public class ScrewDriver : Tool {
        }
        public class ToolHandler {
            public virtual void Tighten<T>(T tool) {
                Console.WriteLine(typeof(T));
            }
        }
   
        public static void Main(string[] args)
        {
            List<Tool> tools = new List<Tool>{new Wrench(), new ScrewDriver()};
       
            var handler = new ToolHandler();
            foreach(var tool in tools){
                handler.Tighten((dynamic)tool);
            }
        }
    }
}

1 comment:

  1. I prefer using the 'as' operator for doing the cast. It avoids having too many brackets and is more readable.

    handler.Tighten(tool as dynamic);

    ReplyDelete