locked
Using A Class Defined In A Dynamically Loaded C# DLL That Implements An Interface RRS feed

  • Question

  • Hi,

    I have created a managed C# DLL that defines an interface (ISomething) and a class that implements it (MyClass).

    Now, I have a C# app that dynamically loads this DLL, and then has to get from it a reference to an object that implements ISomething (this should be, of course, a reference of type ISomething that really references a new object of type MyClass).

    How can my C# app achieve this? (Please detail.)

    Thanks,


    Ofer

    Thursday, May 2, 2013 2:03 PM

Answers

  • This is actually very simple using MEF, which is included in .NET 4+ automatically.

    The overview for this is here: http://msdn.microsoft.com/en-us/library/dd460648.aspx

    It will handle the loading and assigning of the interface reference to types defined in other assemblies for you.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    • Proposed as answer by CoolDadTx Thursday, May 2, 2013 3:54 PM
    • Marked as answer by Ofer Elboher Thursday, May 2, 2013 7:12 PM
    Thursday, May 2, 2013 3:36 PM
  • Again, MEF makes this easy, but you could do it via reflection.

    Once you have the Type for your class, just use:

    ISomething instance = (ISomething)Activator.CreateInstance(theMyClassType);



    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    • Marked as answer by Ofer Elboher Thursday, May 2, 2013 7:12 PM
    Thursday, May 2, 2013 4:04 PM
  • Hello,

    Here is a complete example of a pluggable architecture from scratch, to do that, we're going to create three different projects.

    1. PluggableArchitecture.Interface

    This project provides two things a) the interfaces that plugins can implement b) the engine that responsible to discover and load plugins.

    So the first thing you need to do is define the interface that plugins will implement.

    	public interface IPluginBase
    	{
    		string Name { get; }
    		string Version { get; }
    	}

    Next, we're going to define a new interface that inherits from IPluginBase and we will use it to compute the two numbers.

    	public interface IMath : IPluginBase
    	{
    		int Compute(int arg1, int arg2);
    	}

    Finally, we need to implement the engine that will create the instances based on some rule in this case the rule is any class that implements IPluginBase.

    namespace PluggableArchitecture.Interface
    {
    	using System;
    	using System.IO;
    	using System.Reflection;
    	using System.Linq;
    
    	public static class Plugin
    	{
    		public static T CreatePlugin<T>(string file) {
    
    			T plugin = default(T);
    			
    			Type pluginType = null;
    
    			if(File.Exists(file))
    			{
    				Assembly asm = Assembly.LoadFile(file);
    
    				if(asm != null)
    				{
    					for(int i = 0; i < asm.GetTypes().Length; i++)
    					{
    						Type type = (Type)asm.GetTypes().GetValue(i);
    						
    						if (IsImplementationOf(type, typeof(IPluginBase)))
    						{
    							plugin = (T)Activator.CreateInstance(type);
    						}
    					}
    				}
    			}
    
    			return plugin;
    		}
    		
    		private static bool IsImplementationOf(Type type, Type @interface)
    		{
    			Type[] interfaces = type.GetInterfaces();
    
    			return interfaces.Any(current => IsSubtypeOf(ref current, @interface));
    		}
    
    		private static bool IsSubtypeOf(ref Type a, Type b)
    		{
    			if (a == b)
    			{
    				return true;
    			}
    
    			if (a.IsGenericType)
    			{
    				a = a.GetGenericTypeDefinition();
    
    				if (a == b)
    				{
    					return true;
    				}
    			}
    
    			return false;
    		}
    	}
    }

    2. PluggableArchitecture.Plugin

    We will use this project to create our first plugin that will take two numbers and multiply it.

    	class Math  : IMath
    	{
    		public string Name {
    			get { return "Math"; }
    		}
    
    		public string Version {
    			get { return "1.0.0.0"; }
    		}
    
    		public int Compute(int arg1, int arg2) {
    			return arg1 * arg2;
    		}
    	}

    3. PluggableArchitecture.Host

    This project will use the engine to execute the plugin and print the results.

    namespace PluggableArchitecture.Host
    {
    	using System;
    
    	using PluggableArchitecture.Interface;
    
    	class Program
    	{
    		static void Main(string[] args) {
    			string filePath = Environment.CurrentDirectory + @"\PluggableArchitecture.Plugin.dll";
    
    			IMath math = Plugin.CreatePlugin<IMath>(filePath);
    
    			Console.WriteLine();
    
    			if(math != null)
    			{
    				Console.Write(" Input the 1st number to compute: ");
    				int arg1 = Convert.ToInt32(Console.ReadLine());
    
    				Console.Write(" Input the 2nd number to compute: ");
    				int arg2 = Convert.ToInt32(Console.ReadLine());
    
    				Console.WriteLine();
    				Console.WriteLine(" Results: " + math.Compute(arg1, arg2));
    			}
    			else
    			{
    				Console.WriteLine(" No plugins were found.");
    			}
    
    			Console.ReadKey(true);
    		}
    	}
    }

    Note that nowadays, it's better to use MEF to implement this rather than doing it manually.



    Regards,

    Eyal Shilony


    • Edited by Eyal Solnik Thursday, May 2, 2013 4:32 PM
    • Marked as answer by Ofer Elboher Thursday, May 2, 2013 7:12 PM
    Thursday, May 2, 2013 4:31 PM

All replies

  • try this if you don't want to use spring dot net or Unity.

    var assembly = Assembly.LoadFrom(path);
                    var types = assembly.GetTypes();
                    var something = types.FirstOrDefault(type => type.BaseType == typeof(ISomething));
                    if (something != null)
                    {
    //do something with something
    }


    Faisal Ahmed Farooqui —————————— If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    Thursday, May 2, 2013 2:35 PM
  • Will there be multiple different classes and different DLL's implementing ISomething?

    Do you know them ahead of time?  How and when do you know what DLL to use...and what class to select.

    Thursday, May 2, 2013 2:37 PM
  • Thanks, Faisal.

    I know how to load the DLL and get its types; my problem is in getting a reference to a new object of type MyClass, and use it in my app as a reference to ISomething.


    Ofer

    Thursday, May 2, 2013 3:35 PM
  • This is actually very simple using MEF, which is included in .NET 4+ automatically.

    The overview for this is here: http://msdn.microsoft.com/en-us/library/dd460648.aspx

    It will handle the loading and assigning of the interface reference to types defined in other assemblies for you.


    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    • Proposed as answer by CoolDadTx Thursday, May 2, 2013 3:54 PM
    • Marked as answer by Ofer Elboher Thursday, May 2, 2013 7:12 PM
    Thursday, May 2, 2013 3:36 PM
  • My app checks if a DLL in a pre-determined name may be found in the application directory. If indeed, it loads it. At this point I would like to call some static method that creates a new instance of MyClass, and returns it as a reference to ISomething.

    No additional DLLs will be loaded. At any single session my app uses a single reference of type ISomathing.

    Thanks.


    Ofer

    Thursday, May 2, 2013 3:42 PM
  • Again, MEF makes this easy, but you could do it via reflection.

    Once you have the Type for your class, just use:

    ISomething instance = (ISomething)Activator.CreateInstance(theMyClassType);



    Reed Copsey, Jr. - http://reedcopsey.com
    If a post answers your question, please click "Mark As Answer" on that post and "Mark as Helpful".

    • Marked as answer by Ofer Elboher Thursday, May 2, 2013 7:12 PM
    Thursday, May 2, 2013 4:04 PM
  • Hello,

    Here is a complete example of a pluggable architecture from scratch, to do that, we're going to create three different projects.

    1. PluggableArchitecture.Interface

    This project provides two things a) the interfaces that plugins can implement b) the engine that responsible to discover and load plugins.

    So the first thing you need to do is define the interface that plugins will implement.

    	public interface IPluginBase
    	{
    		string Name { get; }
    		string Version { get; }
    	}

    Next, we're going to define a new interface that inherits from IPluginBase and we will use it to compute the two numbers.

    	public interface IMath : IPluginBase
    	{
    		int Compute(int arg1, int arg2);
    	}

    Finally, we need to implement the engine that will create the instances based on some rule in this case the rule is any class that implements IPluginBase.

    namespace PluggableArchitecture.Interface
    {
    	using System;
    	using System.IO;
    	using System.Reflection;
    	using System.Linq;
    
    	public static class Plugin
    	{
    		public static T CreatePlugin<T>(string file) {
    
    			T plugin = default(T);
    			
    			Type pluginType = null;
    
    			if(File.Exists(file))
    			{
    				Assembly asm = Assembly.LoadFile(file);
    
    				if(asm != null)
    				{
    					for(int i = 0; i < asm.GetTypes().Length; i++)
    					{
    						Type type = (Type)asm.GetTypes().GetValue(i);
    						
    						if (IsImplementationOf(type, typeof(IPluginBase)))
    						{
    							plugin = (T)Activator.CreateInstance(type);
    						}
    					}
    				}
    			}
    
    			return plugin;
    		}
    		
    		private static bool IsImplementationOf(Type type, Type @interface)
    		{
    			Type[] interfaces = type.GetInterfaces();
    
    			return interfaces.Any(current => IsSubtypeOf(ref current, @interface));
    		}
    
    		private static bool IsSubtypeOf(ref Type a, Type b)
    		{
    			if (a == b)
    			{
    				return true;
    			}
    
    			if (a.IsGenericType)
    			{
    				a = a.GetGenericTypeDefinition();
    
    				if (a == b)
    				{
    					return true;
    				}
    			}
    
    			return false;
    		}
    	}
    }

    2. PluggableArchitecture.Plugin

    We will use this project to create our first plugin that will take two numbers and multiply it.

    	class Math  : IMath
    	{
    		public string Name {
    			get { return "Math"; }
    		}
    
    		public string Version {
    			get { return "1.0.0.0"; }
    		}
    
    		public int Compute(int arg1, int arg2) {
    			return arg1 * arg2;
    		}
    	}

    3. PluggableArchitecture.Host

    This project will use the engine to execute the plugin and print the results.

    namespace PluggableArchitecture.Host
    {
    	using System;
    
    	using PluggableArchitecture.Interface;
    
    	class Program
    	{
    		static void Main(string[] args) {
    			string filePath = Environment.CurrentDirectory + @"\PluggableArchitecture.Plugin.dll";
    
    			IMath math = Plugin.CreatePlugin<IMath>(filePath);
    
    			Console.WriteLine();
    
    			if(math != null)
    			{
    				Console.Write(" Input the 1st number to compute: ");
    				int arg1 = Convert.ToInt32(Console.ReadLine());
    
    				Console.Write(" Input the 2nd number to compute: ");
    				int arg2 = Convert.ToInt32(Console.ReadLine());
    
    				Console.WriteLine();
    				Console.WriteLine(" Results: " + math.Compute(arg1, arg2));
    			}
    			else
    			{
    				Console.WriteLine(" No plugins were found.");
    			}
    
    			Console.ReadKey(true);
    		}
    	}
    }

    Note that nowadays, it's better to use MEF to implement this rather than doing it manually.



    Regards,

    Eyal Shilony


    • Edited by Eyal Solnik Thursday, May 2, 2013 4:32 PM
    • Marked as answer by Ofer Elboher Thursday, May 2, 2013 7:12 PM
    Thursday, May 2, 2013 4:31 PM
  • Thank you very-very much, Reed and Eyal. Your answers were most helpfull!

    Ofer

    Thursday, May 2, 2013 7:12 PM