Waleed Naveed
Waleed Naveed

Reputation: 2381

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

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

Answers (3)

Athanasios Kataras
Athanasios Kataras

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

Surendra Shrestha
Surendra Shrestha

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

Abdul Hannan
Abdul Hannan

Reputation: 424

Change the line which is throwing exception to this

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

Upvotes: 0

Related Questions