我正在开发C#应用程序。我有一个ClassLibrary
类型的项目,该项目具有一个接口和一些实现此接口的类。代码是:
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; }
}
我的要求是,我必须找到IUserInterface
的所有实现,使用反射创建这些实现的实例,并将所有这些实例存储在List中。我的代码是:
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();
}
}
我确实设法创建了实例,但是在
处获得了异常IUserInterface typeObj = (IUserInterface)typeObject;
例外是:
System.InvalidCastException:'无法转换类型的对象 'ClassLibrary.UserClass'键入'ClassLibrary.IUserInterface'。
如何解决?我知道我犯了一些愚蠢的错误,但是我无法弄清楚。我进行强制转换的原因是因为我必须将实例保存在IUserInterface
类型列表中。
答案 0 :(得分:1)
我创建了一个程序集实用程序,它可以从.dll文件中获取具有给定接口的所有类的实例。以下是我用来获得这些结果的代码:
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();
}
}
答案 1 :(得分:0)
您需要了解加载上下文。在您的情况下,您必须将同一程序集加载到默认上下文中,以使其了解不仅仅是命名冲突的问题。
有关上下文的完整指南,请查看此处:https://docs.microsoft.com/en-us/dotnet/framework/deployment/best-practices-for-assembly-loading
默认加载上下文包含通过探测全局程序集缓存,主机程序集存储(如果托管了运行时)(例如,在SQL Server中)以及应用程序域的ApplicationBase和PrivateBinPath找到的程序集。大多数Load方法的重载都将程序集加载到此上下文中。
要解决您的问题,请按以下说明使用load
方法:https://docs.microsoft.com/en-us/dotnet/api/system.reflection.assembly.load?view=netframework-4.8#System_Reflection_Assembly_Load_System_String_
答案 2 :(得分:0)
将引发异常的行更改为此
IUserInterface typeObj = (IUserInterface)Activator.CreateInstance(typeObject);