Click here to Skip to main content
15,885,546 members
Articles / Programming Languages / C#
Article

DuckTyping: Runtime Dynamic Interface Implementation

Rate me:
Please Sign up or sign in to vote.
4.98/5 (102 votes)
23 Oct 20067 min read 186.8K   1.3K   135   39
Dynamic typing (DuckTyping) implementation for .NET.

Introduction

This article presents an implementation of runtime Duck-Typing for .NET.

Duck typing is a term for dynamic typing typical of some programming languages, such as Smalltalk, Visual FoxPro, or Coldfusion, where a variable's value itself determines what the variable can do.

Thus, an object having all the methods described in an interface can be made to implement that interface dynamically at runtime, even if the object’s type does not include the interface in its definition.

The term is a reference to the "duck test": "If it walks like a duck and quacks like a duck, it must be a duck." One can also say that the duck typing method ducks the issue of typing variables (from Wikipedia).

Note: Although the library can be used in all CLS compliant languages (C#, VB.NET, and many others), the article and the source code is based on C# 2.0.

Background

In statically typed languages like C#, the interface of a class defines which operations are supported. A supported operation may be calling a method with a specified signature, or getting or setting a property, or be able to attach to a specific event. In most cases, this makes perfect sense, since the compiler is able to verify if the operations are supported by the specified type.

But there are cases in which static typing is very annoying. Consider a method that loads an image into the Image property of a PictureBox:

C#
public static class ImageTools {
    public static void SetImage(PictureBox obj, string resourceName){
        Stream imgStream = 
         typeof(ImageTools).Assembly.GetManifestResourceStream(resourceName);
        Image image = Image.FromStream(imgStream);
    
        obj.Image = image;
    
        // ensure that the Image and the Stream will
        // be closed when the PictureBox is diposed.
        obj.Disposed += delegate(object sender, EventArgs e){
            image.Dispose();
            imgStream.Dispose();
        };
    }
}

// test code
ImageTools.SetImage(this.PictureBox1, "Image");

As long as you know the Type of obj (PictureBox), this code works well. But what if you want to call SetImage with a Label control? The Label control contains both operations we need: the Image property, and the Disposed event, so it sould be possible to use a Label.

But how do we need to write this in code? The first method avoids reflection. It uses the "as" operator to check for each type that needs to be supported.

C#
public static void SetImage(object owner, string resourceName){    
    if (owner == null) throw new ArgumentNullException("owner");
    
    Component ownerAsComponent = owner as Component;
    PictureBox ownerAsPictureBox = owner as PictureBox;
    Label ownerAsLabel = owner as Label;
    
    // validate type of "owner"
    if (ownerAsPictureBox == null && ownerAsLabel == null)
        throw new ArgumentException("Wrong owner Type! " + 
              "Only Labels and PictureBoxes are supported!");
    
    Stream imgStream = 
      typeof(ImageTools).Assembly.GetManifestResourceStream(resourceName);
    Image image = Image.FromStream(imgStream);
    
    if (ownerAsPictureBox != null)
        ownerAsPictureBox.Image = image;
        
    if (ownerAsLabel != null)
        ownerAsLabel.Image = image;
    
    ownerAsComponent.Disposed += delegate(object sender, EventArgs e){
        image.Dispose();
        imgStream.Dispose();
    }; 
}

This method has significant drawbacks:

  • The owner parameter does not expose any Type-information that is expected by the function. You need to check them manually.
  • Every type that has to be supported needs to be specified within the code. When you have to support a new Type, the assembly has to be recompiled.
  • The code gets unnessersary long because of the nessersary type-checking.

Another possibility would be to use reflection. Most code that I've seen which uses a kind of Signature Based Polymorphism uses this approach:

C#
public static void SetImage(object owner, string resourceName){    
    PropertyInfo piImage = owner.GetType().GetProperty("Image");
    EventInfo eiDisposed = owner.GetType().GetEvent("Disposed");
    
    if (piImage == null || piDisposed == null)
        throw new ArgumentException("The 'owner' object" + 
              " is not compatible with the required interface!");
        
    Stream imgStream = 
       typeof(ImageTools).Assembly.GetManifestResourceStream(resourceName);
    Image image = Image.FromStream(imgStream);

    piImage.SetValue(owner, image, new object[0]);
    
    EventHandler disposedHandler = delegate(object sender, EventArgs e){
        image.Dispose();
        imgStream.Dispose();
    };
    
    eiDisposed.AddEventHandler(owner, disposedHandler);
}

By using Reflection, you can avoid having to specify every possible Type within the implementation, but then you have other drawbacks:

  • The owner parameter do not expose any Type-information that is expected by the function. You need to check them manually.
  • Reflection overhead degrades performance on each call.
  • Reflection degrades maintainability (the method has to be implemented different).
  • Some operations (like Indexers) are tricky to handle with Reflection.

So what can be done to solve the problem? Would it not be nice to declare the method like the following snippet?

C#
public static void SetImage([any object with "Image" property 
              and "Disposed" event] obj, string resourceName){
    obj.Image = ....;
}

The first thing which comes in mind of a .NET programmer is to declare an interface (I'll name it IImageContainer) which contains the operations we need within the method. The SetImage method will be modified to take an IImageContainer instance instead of a PictureBox instance:

C#
public interface IImageContainer{
    Image Image{set;}
    event EventHander Disposed;
}

public static class ImageTools {
    public static void SetImage(IImageContainer obj, string resourceName){
        Stream imgStream = 
          typeof(ImageTools).Assembly.GetManifestResourceStream(resourceName);
        Image image = Image.FromStream(imgStream);
    
        obj.Image = image;
    
        // ensure that the Image and the Stream will
        // be closed when the PictureBox is diposed.
        obj.Disposed += delegate(object sender, EventArgs e){
            image.Dispose();
            imgStream.Dispose();
        };
    }
}

The question is: How to get an instance of IImageContainer which can be passed into the method? For a class whose source you can recompile, you can put the IImageContainer interface within the class declaration, but it is impossible to implement the interface when you cannot recompile the class (the Label or the PictureBox classes, for example).

This is where DuckTyping comes into play. For classes which implement all interface-operations, you can create a DuckType by using the DuckTyping.Implement<IImageContainer>(object) method. The method returns an instance of a dynamically constructed type that redirects all operations to the specified object.

C#
// test code
ImageTools.SetImage(
    DuckTyping.Implement<IImageContainer>(this.PictureBox1), "Image");
ImageTools.SetImage(
    DuckTyping.Implement<IImageContainer>(this.Label1), "Image2");

Implementation Details

Before starting, I would like to define the terms being used:

  • DuckedType: A DuckedType is the type for which a DuckType is generated (within the example, this would be the PictureBox or Label types).
  • DuckType: A DuckType is the Type which is generated at runtime, based on the Type of the interface, and the DuckedType.
  • Duck: A Duck is an instance of a DuckType. Ducks are returned by the Implement method.

What happens when Implement gets called? Basically a type (the DuckType) will be generated on-the-fly which takes an instance of the DuckedType within the constructor.

All interface-operations will be redirected to the DuckedType instance that was used to create the Duck. To show you how it works, the DuckType of our example (printed for the PictureBox as the DuckedType) is shown below:

C#
namespace DynamicDucks.IImageContainer {
    public class Duck0 : IImageContainer {
            
        PicturBox _obj; // ducked object
    
        public Duck0(PictureBox obj){
            _obj = obj;
        }
    
        public Image Image {
            set{
                _obj.Image = value;
            }
        }
        
        public event EventHandler Disposed {
            add {_obj.Disposed += value;}
            remove {_obj.Disposed -= value;}
        }
    }
}

Thread Safety

You can see that the DuckType itself holds no internal state - all operations are redirected to the ducked object. So thread safety is not affected by using DuckTyping.

Reflection

Another thing you may have noticed is: Reflection is only used for creating the DuckType, but not for calling the operations. Calling methods on Duck-Types can be made without the performance degradation from reflection.

DuckType Cache

DuckTypes are only created once for each interface and DuckedType. When a DuckType has been compiled successfully, it will be inserted into an internal cache-class. Subsequent calls to the Implement method by using the same types will not compile the DuckType again, but will use the type from the cache.

Optimization Overloads

With the DuckTyping.Implement<TInterface>(obj) method, you can create all DuckTypes you like. In some scenarios, you can optimize the way DuckTyping works by using alternative overloads. How significant the optimization (either in speed or in memory footprint) is, depends on the specific usage.

There are two fields of possible optimization:

Implementing an Array of Ducks with one call

With each call to DuckTyping.Implement<TInterface>(obj), an assembly will be emitted and compiled (when not found in the cache). When you know that there are several Ducks to be created, you can create them in one call. NDuck will emit a single source file for all DuckTypes that need to be created, so the generation and compilation process will happen only once. Because only one additional assembly will be loaded, the program also benefits from a smaller memory footprint.

C#
// sample call with Arrays:
IImageContainer[] ducks = 
   DuckTyping.Implement<IImageContainer>(this.PictureBox1, this.Label1);
IImageContainer pictureBoxDuck = ducks[0];
IImageContainer labelDuck = ducks[0];

ImageTools.SetImage(pictureBoxDuck, "Image");
ImageTools.SetImage(labelDuck, "Image2");

If you want to create serveral needed DuckTypes once, but you do not have an object-instance (e.g., at startup), you can use the DuckTyping.PrepareDuckTypes<TInterface>(params Type[] duckedTypes) method.

Static Specification of the DuckedType

If you call DuckTyping.Implement<TInterface>(obj), the DuckType will be generated from the dynamic type of obj (this is obj.GetType()). In some scenarios, this could result in unnecessary DuckTypes being generated. By specifying the static object Type, you can force NDuck to use a static type instead of the dynamic type.

Consider the following code:

C#
public class MyLabel : Label {
    public void SomeMethod() {}
}

The class MyLabel extents the default Label class. If we let DuckTyping implement a Duck for the IImageContainer interface (we assume that a DuckType for IImageContainer based on the default Label class has already been created and inserted into the cache), DuckTyping would create a new DuckType for the MyLabel class. By specifying the TStaticObjectType Type-parameter, the existing implementation can be used.

C#
IImageContainer duck = 
    DuckTyping.Implement<IImageContainer, Label>(this.MyLabel1);

It would be possible to determine the best DuckedType for any type by reflecting the class hierarchy to get the deepest base-type that declares all needed members. This could be an improvement for the next version.

Requirements and Restrictions

The library is CLS compliant, it can be used by all CLS compliant languages such as C# or VB.NET. The language of your choice has to support Generics (it should be able to call generic methods).

NDuck emits code at runtime which will be compiled dynamically. Because the Compiler needz to access the Type of the interface and the Ducked-Type, these Types must have public scope. If the interface-type is not public, an exception will be thrown.

Because NDuck is implemented by using Generics, you have to know the Type of the interface at compile-time.

Conclusion

By using DuckTyping, you can combine type-safety with flexibility. DuckTyping is not something that you need in every scenario, but there are cases where DuckTyping is very useful. With DuckTyping, you can avoid type-casts (which are not any safer than DuckTyping) and redundant code.

History

  • 2006/10/10
    • Initial release.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Architect
Austria Austria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: Great start; want more! Pin
Chris Wuestefeld14-Nov-06 7:36
Chris Wuestefeld14-Nov-06 7:36 
GeneralClever Pin
GoodTurn224-Oct-06 7:19
GoodTurn224-Oct-06 7:19 
GeneralRe: Clever Pin
GProssliner13-Nov-06 23:12
GProssliner13-Nov-06 23:12 
GeneralSo elegant! Pin
Andrew Tweddle24-Oct-06 0:07
Andrew Tweddle24-Oct-06 0:07 
GeneralRe: So elegant! Pin
GProssliner24-Oct-06 5:37
GProssliner24-Oct-06 5:37 
GeneralRe: So elegant! Pin
firestorm3539-Nov-06 8:28
firestorm3539-Nov-06 8:28 
QuestionWhy delegate? Pin
Orangy23-Oct-06 11:24
Orangy23-Oct-06 11:24 
AnswerRe: Why delegate? Pin
Andrew Smith23-Oct-06 17:25
Andrew Smith23-Oct-06 17:25 
I would think there are several problems with that. The class may be sealed. Or it might not have a public constructor. More importantly, I think the idea here is to manipulate an instance already created. In the example of a picture box, you don't want to replace the instance of the picturebox that you have on the form; this is just a mechanism for manipulating an existing instance of an object that has a known public interface.
AnswerRe: Why delegate? Pin
GProssliner24-Oct-06 5:47
GProssliner24-Oct-06 5:47 
QuestionMultiple inheritence for C#? Pin
Marc Clifton23-Oct-06 10:55
mvaMarc Clifton23-Oct-06 10:55 
AnswerRe: Multiple inheritence for C#? Pin
GProssliner24-Oct-06 6:01
GProssliner24-Oct-06 6:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.