Felipe Deveza
Felipe Deveza

Reputation: 1969

How to dynamically instantiate a class?

var typeName = $"Drv{id}.Fonte";
var type = Type.GetType(typeName);
var myObject = Activator.CreateInstance(type);

Drv{id} is my namespace and Fonte is the class name.

Why 'type' is always null?

When i call the third line, I get this:

System.ArgumentNullException: 'Value cannot be null.'

enter image description here

enter image description here

Upvotes: 0

Views: 557

Answers (4)

Reinstate Monica Cellio
Reinstate Monica Cellio

Reputation: 26153

Have this bit of code I wrote recently. I needed to instantiate an object at runtime, like you, but came across several issues with the location of the class (different assemblies, running as vshost, running as IISExpress).

This was for a VERY generic scenario, so it may be overkill for your needs...

private static Type GetType(string typeName)
{
    // check executing assembly
    var type = Assembly
        .GetExecutingAssembly()
        .GetTypes()
        .FirstOrDefault(x => x.FullName == typeName);

    // if not found check referenced assemblies
    if (type == null)
    {
        type = Assembly
            .GetExecutingAssembly()
            .GetReferencedAssemblies()
            .Select(Assembly.Load)
            .SelectMany(x => x.GetTypes())
            .FirstOrDefault(x => x.FullName == typeName);
    }

    // if still not found check all suitably named assemblies in executing folder
    if (type == null)
    {
        var files = Directory.GetFiles(AppDomain.CurrentDomain.BaseDirectory, "*.dll");

        if (files.Length == 0)
        {
            files = Directory.GetFiles(AppDomain.CurrentDomain.RelativeSearchPath, "*.dll");
        }

        files.ToList().ForEach(filename =>
        {
            if (type == null)
            {
                var assembly = Assembly.LoadFile(filename);
                var castableAssembly = AppDomain.CurrentDomain.Load(assembly.GetName());
                type = castableAssembly.GetTypes().FirstOrDefault(x => x.FullName == typeName);
            }
        });
    }

    return type;
}

That will return either null or a type that you can use with Activator.CreateInstance() and you know it will be accessible. In your case, change your code to...

var typeName = $"Drv{id}.Fonte";
var type = GetType(typeName); // using the above method
var myObject = Activator.CreateInstance(type);

This is not performance friendly code, but unless you're doing this a lot, or your working directory has a tonne of assemblies you shouldn't need to worry about it. If you are doing it a lot or you do have a tonne of assemblies then you should maybe reconsider the overall design.

Upvotes: 1

kuskmen
kuskmen

Reputation: 3775

I will elaborate more on the practical part.

As others said you need the assembly qualified name. Why? Well look at the comment of Type.GetType(string typeName) method.

Parameters:

// typeName:
// The assembly-qualified name of the type to get. See System.Type.AssemblyQualifiedName.

// If the type is in the currently executing assembly or in Mscorlib.dll, it is sufficient to supply the type name qualified by its namespace.

Notice that only if type is in the currently executing assembly is sufficient to call it like you did.

I have a class in another project that is referenced by my main project just like you do.

using System.Reflection;

namespace Test2
{
    public class Class1
    {
        public string CallMe()
        {
            return Assembly.GetExecutingAssembly().FullName;
        }
    }
}

Then I call it like that:

using System;
using System.Reflection;
using Test2;

namespace NetCore2._0
{
    class Program
    {
        static void Main(string[] args)
        {
            var typeName = typeof(Test2.Class1).AssemblyQualifiedName;
            var type = Type.GetType(typeName);

            Console.WriteLine(Assembly.GetExecutingAssembly().FullName);
            var myObject = Activator.CreateInstance(type) as Class1;
            Console.WriteLine(myObject.CallMe());
        }

    }
}

I hope you can see where the difference is and why you need to supply qualified name now.

Upvotes: 2

ispiro
ispiro

Reputation: 27713

You need the Assembly qualified name. As mentioned on MSDN.

Example (from the MSDN link):

TopNamespace.SubNameSpace.ContainingClass+NestedClass,MyAssembly

Upvotes: 1

JuanR
JuanR

Reputation: 7803

The format of the string you are using to instantiate the type is wrong.

From the documentation of Type.GetType(String):

typeName : String

The assembly-qualified name of the type to get. See AssemblyQualifiedName. If the type is in the currently executing assembly or in Mscorlib.dll, it is sufficient to supply the type name qualified by its namespace.

Source: Type.GetType Method

From AssemblyQualifiedName's documentation:

The assembly-qualified name of a type consists of the type name, including its namespace, followed by a comma, followed by the display name of the assembly. The display name of an assembly is obtained using the Assembly.FullName property.

For example, the assembly-qualified name for a class might look like this:

TopNamespace.SubNameSpace.ContainingClass+NestedClass, MyAssembly, Version=1.3.0.0, Culture=neutral, PublicKeyToken=b17a5c561934e089

Upvotes: 1

Related Questions