Click here to Skip to main content
15,868,083 members
Articles / Programming Languages / C#

C# Object to Interface Caster Utility

Rate me:
Please Sign up or sign in to vote.
4.94/5 (21 votes)
28 Apr 2009CPOL3 min read 59.2K   528   44   22
A utility that casts an object to an interface, even if it doesn't formally implement it.

Introduction

In this article, I describe a unique way of casting any object to an interface that it actually exposes, even though it does not formally implement the particular interface. In order to get an idea of what I’m trying to propose here, consider the examples below.

Let's say you’re utilizing third party libraries (LibA and LibB) that are dealing with persons. These libraries have the PersonLibA and PersonLibB classes defined, and are exposing instances of these persons. Most likely, they would expose a similar public interface and would have properties like Name, EMail, Address, DateOfBirth, etc. I’m almost sure that you would like to access these persons uniformly. If you have the source code of these components, you could define a common interface and implement the PersonLibA and PersonLibB classes from your newly defined interface. However, if the source code is not available, that would not work out. The only thing left to do is to define wrapper classes for each of the person classes and implement the common interface.

Similarly, if you want to access the Text property of a WPF TextBox and TextBlock uniformly, you would most probably need to define two wrapper classes to expose the Text property.

It can be done easily if there are not so many third party classes involved and the common interface is simple enough. Otherwise, this process would be really painful. What I suggest here is a utility class that casts an object to an interface by generating a wrapper class on the fly and doing all the dirty work for you behind the scenes. I will go straight to the usage, and later on will present some tricks used in the solution.

Using the code

The utility comes as a template class where T is the the interface to convert to. It has a single public method, T As(object o), which gets any kind of object, and returns the converted wrapper object if succeeded.

C#
public static class ObjectCaster<T>
{
    public static T As(object o)
    {
        // ....
    }
}

Consider the following Person class. Note that it does not implement any interface.

C#
public class Person
{
    public string Name { get; set; }

    public event EventHandler Initialized;

    public void Initialize()
    {
        Name = "Initialized";
        EventHandler e = Initialized;
        if (e != null)
            e(this, EventArgs.Empty);
    }
}

However, it is perfectly aligned with the following interface:

C#
public interface IPerson
{
    string Name { get; set; }

    event EventHandler Initialized;

    void Initialize();
}

Below is the actual demo code which utilizes the ObjectCaster class and converts an instance of the Person class to the IPerson interface (line 4).

C#
Person obj = new Person() { Name = "John" };
Console.WriteLine(string.Format("(obj is IPerson) == {0}", obj is IPerson));

IPerson iperson = ObjectCaster<IPerson>.As(obj);
iperson.Initialized += ((sender, e) => Console.WriteLine("Initialized Called"));

Console.WriteLine("Person> " + obj.Name);
Console.WriteLine("IPerson> " + iperson.Name);

iperson.Name = "Steve";

Console.WriteLine("Person> " + obj.Name);
Console.WriteLine("IPerson> " + iperson.Name);

iperson.Initialize();

Console.WriteLine("Person> " + obj.Name);
Console.WriteLine("IPerson> " + iperson.Name);

As you see the output of the code above, you will easily notice that the object does not formally implement the IPerson interface, and correctly wraps the properties, methods, and events. You may also notice that the wrapped event handler modifies the value of the original sender (which is an instance of Person) to an instance of the converted wrapper object (iperson variable).

binary_CSharp_Object_Caster

Background

The key point of the solution is concentrated in the GenerateProxyClass method:

C#
private static CodeCompileUnit GenerateProxyClass(Type interfaceType, Type sourceType)
{
    //...
}

Basically, it generates a new proxy class using the System.CodeDom machinery, which wraps the sourceType and implements the interfaceType interface. With the help of .NET Reflection, it goes over the members of the interfaceType type and generates the corresponding members in the proxy class. After that, it compiles the generated class utilizing the CodeDomProvider class and calling the CompileAssemblyFromDom method. At this point, the generated class is compiled, and the last thing to do is to create a new instance of the proxy class and provide the source object as a constructor. The full source code for the utility class and the demo application are available above in the downloads section.

Future work

The next thing to do on this topic is to provide the ability to remap member naming and add type conversion. It will allow to cast to an interface even though the name and type of the members are not the same in the interface and the source classes.

Origin

The origin of this article can be found at: C# Object to Interface Caster Utility.

History

  • 26 April, 2009 - Fixed a bug to support non GACed assemblies. Thanks to Oleg Shilo.
  • 25 April, 2009 - Initial version.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) Independent Contractor
United States United States

Comments and Discussions

 
GeneralThoughts Pin
PIEBALDconsult27-Apr-09 17:12
mvePIEBALDconsult27-Apr-09 17:12 
GeneralRe: Thoughts Pin
Ruben Hakopian27-Apr-09 18:17
Ruben Hakopian27-Apr-09 18:17 
GeneralRe: Thoughts [modified] Pin
_groo_28-Apr-09 2:23
_groo_28-Apr-09 2:23 
GeneralRe: Thoughts Pin
Ruben Hakopian28-Apr-09 7:50
Ruben Hakopian28-Apr-09 7:50 
GeneralRe: Thoughts Pin
_groo_28-Apr-09 20:15
_groo_28-Apr-09 20:15 
GeneralRe: Thoughts Pin
PIEBALDconsult28-Apr-09 4:50
mvePIEBALDconsult28-Apr-09 4:50 
GeneralRe: Thoughts Pin
Ruben Hakopian28-Apr-09 7:42
Ruben Hakopian28-Apr-09 7:42 
GeneralRe: Thoughts Pin
_groo_28-Apr-09 2:35
_groo_28-Apr-09 2:35 
GeneralRe: Thoughts Pin
PIEBALDconsult28-Apr-09 4:45
mvePIEBALDconsult28-Apr-09 4:45 
GeneralRe: Thoughts Pin
Ruben Hakopian28-Apr-09 7:59
Ruben Hakopian28-Apr-09 7:59 
QuestionHave you checked LinFu? Pin
_groo_27-Apr-09 1:21
_groo_27-Apr-09 1:21 
AnswerRe: Have you checked LinFu? Pin
Ruben Hakopian27-Apr-09 10:42
Ruben Hakopian27-Apr-09 10:42 
GeneralWorking with non-GAC assemblies Pin
Oleg Shilo26-Apr-09 16:49
Oleg Shilo26-Apr-09 16:49 
GeneralRe: Working with non-GAC assemblies Pin
Ruben Hakopian26-Apr-09 17:26
Ruben Hakopian26-Apr-09 17:26 
GeneralRe: Working with non-GAC assemblies Pin
Nguyen200926-Aug-09 5:32
Nguyen200926-Aug-09 5:32 
GeneralThis is huge... Pin
Oleg Shilo26-Apr-09 14:17
Oleg Shilo26-Apr-09 14:17 
GeneralRe: This is huge... Pin
Ruben Hakopian26-Apr-09 16:59
Ruben Hakopian26-Apr-09 16:59 
GeneralRe: This is huge... Pin
_groo_28-Apr-09 2:31
_groo_28-Apr-09 2:31 
GeneralRe: This is huge... Pin
Oleg Shilo28-Apr-09 3:18
Oleg Shilo28-Apr-09 3:18 
GeneralRe: This is huge... Pin
Ruben Hakopian28-Apr-09 7:31
Ruben Hakopian28-Apr-09 7:31 
GeneralInteresting idea Pin
User 304249825-Apr-09 16:24
User 304249825-Apr-09 16:24 
GeneralRe: Interesting idea Pin
Ruben Hakopian25-Apr-09 17:04
Ruben Hakopian25-Apr-09 17:04 

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.