'System.InvalidCastException:'无法将类型为'ClassName'的对象强制转换为InterfaceName'。

时间:2019-12-26 07:10:18

标签: c# reflection interface

我正在开发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类型列表中。

3 个答案:

答案 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);