I'd like to load some plugin (C# assembly) at runtime and use some of the classes implementing a certain interface inside each of them. So I wrote a base assemby (Interfaces.dll) in which I declare base interfaces:
namespace Interfaces
{
public interface IPlugin01
{
string Name { get; }
string Description { get; }
void Calc1();
}
public interface IPlugin02
{
void Calc2();
}
}
Then, in two different assemblies (Plugin01.dll and Plugin02.dll) I've implemented those interfaces using classes:
namespace Plugin01
{
public class Class1 : Interfaces.IPlugin01,Interfaces.IPlugin02
{
public string Name { get { return "Plugin01.Class1"; } }
public string Description { get { return "Plugin01.Class1 description"; } }
public void Calc1() { Console.WriteLine("sono Plugin01.Class1 Calc1()"); }
public void Calc2() { Console.WriteLine("sono Plugin01.Class1 Calc2()"); }
}
public class Class2 : Interfaces.IPlugin01
{
public string Name { get { return "Plugin01.Class2"; } }
public string Description { get { return "Plugin01.Class2 description"; } }
public void Calc1() { Console.WriteLine("sono Plugin01.Class2 Calc1()"); }
}
}
and
namespace Plugin02
{
public class Class1 : Interfaces.IPlugin01
{
public string Name { get { return "Plugin02.Class1"; } }
public string Description { get { return "Plugin02.Class1 description"; } }
public void Calc1() { Console.WriteLine("sono Plugin02.Class1 Calc1()"); }
}
public class Class2 : Interfaces.IPlugin02
{
public void Calc2() { Console.WriteLine("sono Plugin02.Class2 Calc2()"); }
}
}
Finally the test console app:
//#define LIST1
#define LIST2
using System;
using System.Collections.Generic;
using System.Reflection;
using System.IO;
using System.Diagnostics;
namespace Test
{
class Program
{
static void Main(string[] args)
{
#if LIST1
List<Interfaces.IPlugin01> list1 = GetFilePlugins<Interfaces.IPlugin01>(@".\Plugins\Plugin01.dll");
#else
List<Interfaces.IPlugin01> list1 = new List<Interfaces.IPlugin01>();
#endif
#if LIST2
List<Interfaces.IPlugin01> list2 = GetFilePlugins<Interfaces.IPlugin01>(@".\Plugins\Plugin02.dll");
#else
List<Interfaces.IPlugin01> list2 = new List<Interfaces.IPlugin01>();
#endif
/// ------------------------------------------------------------------------------
/// If I don't load one of the assembly using LIST1 or LIST2
/// GetDirectoryPlugins returns an empty list.
/// The bug (is a bug or my mistake?) is inside GetFilePlugins:
/// typeT.IsAssignableFrom(type) returns FALSE even if interface is implemented !!!
/// Using LIST1 or LIST2 before using GetDirectoryPlugins makes
/// typeT.IsAssignableFrom(type) return TRUE as expected !!!
/// I'm going crazy over this....
/// ------------------------------------------------------------------------------
List<Interfaces.IPlugin01> listtot = GetDirectoryPlugins<Interfaces.IPlugin01>(@".\Plugins\");
#if LIST1
Console.WriteLine("--- 001 ---");
foreach(Interfaces.IPlugin01 plugin in list1)
plugin.Calc1();
#endif
#if LIST2
Console.WriteLine("--- 002 ---");
foreach (Interfaces.IPlugin01 plugin in list2)
plugin.Calc1();
#endif
Console.WriteLine("--- TOT ---");
foreach (Interfaces.IPlugin01 plugin in listtot)
plugin.Calc1();
Console.ReadLine();
}
public static List<T> GetFilePlugins<T>(string filename)
{
List<T> ret = new List<T>();
if (File.Exists(filename))
{
Type typeT = typeof(T);
Assembly ass = Assembly.LoadFrom(filename);
foreach (Type type in ass.GetTypes())
{
if (!type.IsClass || type.IsNotPublic) continue;
Debug.Print("{0} <- {1}", typeT.IsAssignableFrom(type), type);
if (typeT.IsAssignableFrom(type))
{
T plugin = (T)Activator.CreateInstance(type);
ret.Add(plugin);
}
}
}
return ret;
}
public static List<T> GetDirectoryPlugins<T>(string dirname)
{
List<T> ret = new List<T>();
string[] dlls = Directory.GetFiles(dirname, "*.dll");
foreach (string dll in dlls)
{
List<T> dll_plugins = GetFilePlugins<T>(Path.GetFullPath(dll));
ret.AddRange(dll_plugins);
}
return ret;
}
}
}
As written in comment, if I leave both rows #define LIST1 and #define LIST2 commented, IsAssignableFrom() returns false even if my class implements desired interface. Why?