Note that there are some explanatory texts on larger screens.

plurals
  1. POType.IsAssignableFrom() bug
    text
    copied!<p>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:</p> <pre><code>namespace Interfaces { public interface IPlugin01 { string Name { get; } string Description { get; } void Calc1(); } public interface IPlugin02 { void Calc2(); } } </code></pre> <p>Then, in two different assemblies (Plugin01.dll and Plugin02.dll) I've implemented those interfaces using classes:</p> <pre><code>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()"); } } } </code></pre> <p>and</p> <pre><code>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()"); } } } </code></pre> <p>Finally the test console app:</p> <pre><code>//#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&lt;Interfaces.IPlugin01&gt; list1 = GetFilePlugins&lt;Interfaces.IPlugin01&gt;(@".\Plugins\Plugin01.dll"); #else List&lt;Interfaces.IPlugin01&gt; list1 = new List&lt;Interfaces.IPlugin01&gt;(); #endif #if LIST2 List&lt;Interfaces.IPlugin01&gt; list2 = GetFilePlugins&lt;Interfaces.IPlugin01&gt;(@".\Plugins\Plugin02.dll"); #else List&lt;Interfaces.IPlugin01&gt; list2 = new List&lt;Interfaces.IPlugin01&gt;(); #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&lt;Interfaces.IPlugin01&gt; listtot = GetDirectoryPlugins&lt;Interfaces.IPlugin01&gt;(@".\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&lt;T&gt; GetFilePlugins&lt;T&gt;(string filename) { List&lt;T&gt; ret = new List&lt;T&gt;(); 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} &lt;- {1}", typeT.IsAssignableFrom(type), type); if (typeT.IsAssignableFrom(type)) { T plugin = (T)Activator.CreateInstance(type); ret.Add(plugin); } } } return ret; } public static List&lt;T&gt; GetDirectoryPlugins&lt;T&gt;(string dirname) { List&lt;T&gt; ret = new List&lt;T&gt;(); string[] dlls = Directory.GetFiles(dirname, "*.dll"); foreach (string dll in dlls) { List&lt;T&gt; dll_plugins = GetFilePlugins&lt;T&gt;(Path.GetFullPath(dll)); ret.AddRange(dll_plugins); } return ret; } } } </code></pre> <p>As written in comment, if I leave both rows <strong><em>#define LIST1</em></strong> and <strong><em>#define LIST2</em></strong> commented, IsAssignableFrom() returns false even if my class implements desired interface. Why?</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload