Using Reflection - without paying the price for it.

Posted on 3/18/2007 @ 11:01 PM in #Vanilla .NET by | Feedback | 14289 views

Well this may be old news to many of you really smart guys, but I like this application design so much, that I just had to blog about it.

So the choice is generally between ultimate flexibility (100% reflection), or better performance (add reference, screw reflection).

Here is a good tradeoff between both. It lets you use Reflection, without making you pay the associated performance penalty.

a) Define a base class. For instance, I am writing anapplication that will move content between various blog providers. So at the very heart of it, I have a BlogMoverEngine base class as follows -

public abstract class BlogMoverEngine

{

    public virtual string EngineName

    {

        get { return this.GetType().ToString(); }

    }

 

    public abstract bool IsConfigured { get; }

 

    public abstract Form ConfigurationForm { get; }

 

    public abstract BlogMLBlog ExtractBlogContent();

}

b) Great, now go ahead and actually implement an Engine based on the above base class. For instance, one blog engine could be CommunityServer, so you'd create a class called CommunityServerEngine that inherits from the above BlogMoverEngine class.

c) Awesome. Now don't add reference to the app that uses your CommunityServerEngine. Instead, throw the DLL in a directory called Engines. This can be made automatic via a Custom Build Step that looks like this - copy $(TargetDir)\*.dll $(SolutionDir)\BlogMoverUI\Engines

d) Very nice, now write a LoadEngines method in the client app that has access to the "Engines" directory. Here's the method -

sourceEngines = new BO.Engines();

 

string[] fileNames = Directory.GetFiles(@"C:\PlayArea\BlogMover\BlogMoverUI\Engines", "*.dll");

 

foreach (string fileName in fileNames)

{

    Assembly engineAssembly = Assembly.LoadFile(fileName);

    Type[] allTypes = engineAssembly.GetExportedTypes();

 

    foreach (Type classType in allTypes)

    {

        if (classType.IsSubclassOf(typeof(BlogMoverEngine)))

        {

            BlogMoverEngine engineInstance = engineAssembly.CreateInstance(classType.ToString()) as BlogMoverEngine;

            if (engineInstance != null)

            {

                sourceEngines.Add(engineInstance);

            }

        }

    }

}

Note: BO.Engines is simply of type List<BlogMoverEngine> type

e) Great! Now, what the above code let you do is, it let you create an instance of the appropriate classes in the dlls in the Engines directory. I can now go ahead and databind this to a combobox (just say for example), as shown below:

sourceComboBox.DataSource = sourceEngines;

sourceComboBox.DisplayMember = "EngineName";

f) And then to use the above instances that were created using reflection, in a strongly typed manner, do the below -

void sourceComboBox_SelectedIndexChanged(object sender, EventArgs e)

{

    ComboBox cmbSender = sender as ComboBox;

    BlogMoverEngine blogMoverEngine = cmbSender.SelectedItem as BlogMoverEngine;

 

    if (blogMoverEngine != null)

    {

         // Use the engine in a strongly typed manner

    }

}

Awesome! You can now instantiate and use classes using reflection, but not pay the price for reflection when you actually use them. Instantiation is literally a single method (constructor call), so the overall cost isn't that high. Damn Cool! To use the above in non-full-trust environments, you can isolate the reflection factory in a seperate assembly and give it elevated rights, so your app in general can run under minimal trust, and still use the above.

Sound off but keep it civil:

Older comments..


On 3/19/2007 4:36:41 AM Daniel Moth said ..
Although not exactly identical, this is extremely similar to the way we add plugins. If you are interested in this, you should check out the "Orcas" System.AddIn implementation (not necessarily for usage, but for inspiration from the design :)) - link to it is on my blog


On 3/19/2007 8:59:12 AM Sahil Malik said ..
Neat Daniel, I'll check it out. Thanks :)


On 3/19/2007 6:48:44 PM Jeff Schoolcraft said ..
Maybe irrelevant, but have you seen BlogML sounds like it might be in the same space as your blog mover application...


http://codeplex.com/Wiki/View.aspx?ProjectName=BlogML


On 3/19/2007 7:20:34 PM Liming Xu said ..
Interesting.. I'll have to take a look at Spring.NET and see how they did the reflection in their source code. I noticed from time to time, it's slow (but it coudl be affected by the fact it's trying to integrate with nhibernate though).


On 3/19/2007 8:58:33 PM Sahil Malik said ..
Jeff - My app uses BlogML :), so no co-incidence.


On 3/21/2007 12:23:59 AM lb said ..
Jeff, Sahil -- glad to see that BlogML is being used. I was going to leave a comment saying


?? "move content between various blog providers" ??


!! sounds like you need BlogML !!


On 3/21/2007 10:29:59 AM Sahil Malik said ..
Yeah LB :), but I don't want to create any expectations yet. I am writing this in my spare (?) time.