繁体   English   中英

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

[英]System.InvalidCastException: 'Unable to cast object of type 'ClassName' to type InterfaceName'.'

我正在开发一个 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所有实现,使用反射创建这些实现的实例并将所有这些实例存储在一个列表中。 我的代码是:

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类型列表中。

我创建了一个程序集实用程序,它可以从 .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();
    }
}

您需要了解加载上下文。 在您的情况下,您必须将相同的程序集加载到默认上下文中才能理解这不仅仅是命名冲突的问题。

有关上下文的完整指南,请在此处查看: 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_

将抛出异常的行更改为此

IUserInterface typeObj =  (IUserInterface)Activator.CreateInstance(typeObject);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM