Reputation: 2381
I am working on a C# application. I have a ClassLibrary
type project which has an interface and some classes which implement this interface. Code is:
public interface IUserInterface
{
String GetName();
int Function(int a, int b);
}
public class UserClass : IUserInterface
{
public String GetName() { return "Add"; }
public int Function(int a, int b) { return a + b; }
}
public class UserClass1 : IUserInterface
{
public String GetName() { return "Add"; }
public int Function(int a, int b) { return a + b; }
}
public class UserClass2 : IUserInterface
{
public String GetName() { return "Add"; }
public int Function(int a, int b) { return a + b; }
}
My requirement is that i have to find all the implementations of IUserInterface
, create the instances of those implementations using reflection and store all these instances in a List. My code for this is:
class Program
{
private static List<Type> nameTypeDict = new List<Type>();
private static List<IUserInterface> instances = new List<IUserInterface>();
static void Main(string[] args)
{
Program obj = new Program();
Assembly ass = Assembly.LoadFrom("ClassLibrary.dll");
foreach (Type t in ass.GetExportedTypes())
{
if (t.GetInterface("IUserInterface", true) != null)
{
Console.WriteLine("Found Type: {0}", t.Name);
nameTypeDict.Add(t);//Add to Dictonary
}
}
foreach (var type in nameTypeDict)
{
var typeObject = Activator.CreateInstance(type);
IUserInterface typeObj = (IUserInterface)typeObject;
instances.Add(typeObject);
}
Console.ReadKey();
}
}
I did manage to create the instance, but gets exception at
IUserInterface typeObj = (IUserInterface)typeObject;
The exception is:
System.InvalidCastException: 'Unable to cast object of type 'ClassLibrary.UserClass' to type 'ClassLibrary.IUserInterface'.'
How can this be resolved ? I know i am making some silly mistake but, i am unable to figure it out. The reason why i am casting it is because i have to save the instance in the IUserInterface
type list.
Upvotes: 0
Views: 2373
Reputation: 26450
You need to understand the loading contexts. In your case you must load the same assembly into the default context for it to understand that it's not just a matter of naming collision.
For a full guide on context, check here: https://learn.microsoft.com/en-us/dotnet/framework/deployment/best-practices-for-assembly-loading
The default load context contains assemblies found by probing the global assembly cache, the host assembly store if the runtime is hosted (for example, in SQL Server), and the ApplicationBase and PrivateBinPath of the application domain. Most overloads of the Load method load assemblies into this context.
To solve your issue, use the load
method as described here: https://learn.microsoft.com/en-us/dotnet/api/system.reflection.assembly.load?view=netframework-4.8#System_Reflection_Assembly_Load_System_String_
Upvotes: 0
Reputation: 1080
I have created an assembly utils which can get instances of all classes with the given interface from a .dll file. Following is the code I used to obtain those results:
public static class AssemblyUtils
{
/// <summary>
/// Get Application folder path
/// </summary>
/// <returns></returns>
public static string GetExeFilePath()
{
return Path.GetDirectoryName(Assembly.GetEntryAssembly()?.Location);
}
/// <summary>
/// Get all types of object that implement the type <typeparamref name="T"/> inside the assembly
/// </summary>
/// <typeparam name="T">Interface type</typeparam>
/// <param name="filename">File path of the assembly</param>
/// <returns>An enumerable of type <see cref="Type"/> that implements <typeparamref name="T"/></returns>
public static IEnumerable<Type> GetTypesFromAssembly<T>(string filename)
{
if (!File.Exists(filename))
throw new FileNotFoundException($"DLL file not found. File path: {filename}");
var asm = Assembly.LoadFrom(filename);
var types = asm.GetTypes();
var typeFilter = new TypeFilter(InterfaceFilter);
foreach (var type in types)
{
var interfaces = type.FindInterfaces(typeFilter, typeof(T).ToString());
if (interfaces.Length > 0)
// We found the type that implements the interface
yield return type;
}
}
/// <summary>
/// Creates an <see cref="IEnumerable{T}"/> instance that implements interface of type <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Type of object to find and return</typeparam>
/// <param name="filename">Name of the assembly file</param>
/// <returns>Instance of <see cref="IEnumerable{T}"/></returns>
public static IEnumerable<T> GetInterfacesFromAssembly<T>(string filename, params object[] args)
{
var types = GetTypesFromAssembly<T>(filename);
foreach (var type in types)
{
yield return (T)Activator.CreateInstance(type, args);
}
}
/// <summary>
/// Creates an <see cref="IEnumerable{T}"/> instance that implements interface of type <typeparamref name="T"/>
/// </summary>
/// <typeparam name="T">Type of object to find and return</typeparam>
/// <param name="directory">Path of directory containing assemblies</param>
/// <returns>Instance of <see cref="IEnumerable{T}"/></returns>
public static IEnumerable<T> GetInterfacesFromAssemblyDirectory<T>(string directory, params object[] args)
{
if (!Directory.Exists(directory))
{
throw new DirectoryNotFoundException($"Directory could not be found. Path: { directory }");
}
return Directory.GetFiles(directory, "*.dll").SelectMany(filename => GetInterfacesFromAssembly<T>(filename, args));
}
/// <summary>
/// Type filter for filtering the interface in a class
/// </summary>
/// <param name="typeObj">Object type</param>
/// <param name="criteriaObj">Filter criteria</param>
/// <returns>Whether the criteria is meet or not</returns>
private static bool InterfaceFilter(Type typeObj, object criteriaObj)
{
return typeObj.ToString() == criteriaObj.ToString();
}
}
Upvotes: 1
Reputation: 424
Change the line which is throwing exception to this
IUserInterface typeObj = (IUserInterface)Activator.CreateInstance(typeObject);
Upvotes: 0