简体   繁体   中英

Create class instance in assembly from string name

I'm not sure if this is possible, and I'm quite new to using assemblies in C#.NET.

What I would like to do is to create an instance of a class when supplied the string name of that class. Something like this:

using MyAssembly;

namespace MyNameSpace
{
     Class MyClass
     {
          int MyValue1;
          int MyValue2;

          public MyClass(string myTypeName)
          {
               foreach(Type type in MyAssembly)
               {
                    if((string)type == myTypeName)
                    {
                         //create a new instance of the type
                    }
               }
               AssignInitialValues(//the type created above)
          }

          //Here I use an abstract type which the type above inherits from
          private void AssignInitialValues(AbstractType myClass)
          {
               this.value1 = myClass.value1;
               this.value2 = myClass.value2;
          }
      }
 }

Obviously you cannot compare strings to types but it illustrates what I'm trying to do: create a type found in a different assembly from a supplied string.

Any thoughts?


EDIT:

After attempting:

var myObject = (AbstractType) Activator.CreateInstance(null, myTypeName);
AssignInitialValues(myObject);

I get a number of errors:

  • Inconsistent accessibility: parameter type 'MyAssembly.AbstractType' is less accessible than method 'MyNameSpace.MyClass.AssignInitialValues(MyAssembly.AstractType)'
  • 'MyAssembly.AstractType' is inaccessible due to it's protection level
  • The type or namespace name 'MyAssembly' could not be found (are you missing a using directive or an assembly reference?)
  • The type or namespace name 'AbstractType' could not be found (are you missing a using directive or an assembly reference?)

Not exactly sure why it can't find the assembly; I've added a reference to the assembly and I use a Using Directive for the namespace in the assembly. As for the protection level, it's calling classes (or rather the constructors of classes) which can only be public.

Any clues on where the problem is?


UPDATE:

After looking through several articles on SO I came across this: https://stackoverflow.com/a/1632609/360627 Making the AbstractType class public solved the issue of inconsistent accessibility.

The new compiler error is this:

Cannot convert type 'System.Runtime.Remoting.ObjectHandle' to 'MyAssembly.AbstractType'

The line it references is this one:

var myObject = (AbstractType) Activator.CreateInstance(null, myTypeName);

Using .Unwrap() get's me past this error and I think it's the right way to do it (uncertain). However, when running the program I then get a TypeLoadException when this code is called.

TypeLoadException: Could not load type 'AbstractType' from assembly 'MyNameSpace'...

Right away I can spot that the type its looking for is correct but the assembly it's looking in is wrong. Looking up the Activator.CreateInstance(String, String) method revealed that the null as the first argument means that the method will look in the executing assembly. This is contrary to the required behavior as in the original post.

I've tried using MyAssembly as the first argument but this produces the error:

'MyAssembly' is a 'namespace' but is used like a 'variable'

I think that this is caused by MyAssembly not being a string. However if I try 'MyAssembly' I get the following compiler errors:

  • Too many characters in character literal
  • The best overloaded method match for 'System.Activator.CreateInstance(System.ActivationContext, string[])' has some invalid arguments
  • Argument 1: cannot convert from 'char' to 'System.ActivationContext'
  • Argument 2: cannot convert from 'string' to 'string[]'

Seems to me like it's trying to use the wrong overload.

Any thoughts on how to fix this?

如果您拥有类型的完全限定名称,则根本不需要此foreach循环:

var myObject = (SomeBaseType) Activator.CreateInstance(null, myTypeName);

There are many ways to accomplish your need. Not the best performance :

Activator.CreateInstance(null, myTypeName);

You can also working with this:

var ci = Type.GetType(myTypeName).GetConstructor(Type.EmptyTypes);
var myTypeInstance = ci.Invoke(new object[]{})

Okay so this is the answer. I've tested it using real code and it works. I'm not sure if it's the best way or the most efficient way. I've upvoted the other two answers because they helped me get onto the right track but here is the full answer.

using MyAssembly;

namespace MyNameSpace
{
     Class MyClass
     {
          int MyValue1;
          int MyValue2;

          public MyClass(string myTypeName)
          {
               string assemblyName = Assembly.GetAssembly(typeof(AbstractType)).ToString();
               AbstractType selectedClass = (AbstractType)Activator.CreateInstance(assemblyName, myTypeName).Unwrap();
               AssignInitialValues(selectedClass);
          }

          private void AssignInitialValues(AbstractType myClass)
          {
               this.value1 = myClass.value1;
               this.value2 = myClass.value2;
          }
      }
 }

Took me three days of googling, searching through SO and MSDN but there it is. Note that when instantiating a class with Activator.CreateInstance(String, String) the assembly name (first parameter) must be the full assembly name not just the name of the .dll (or .exe).

The assembly name can be retrieved by using string assemblyName = Assembly.GetAssembly(typeof(AbstractType)).ToString(); where AbstractType can be replaced by a type you know to be contained and defined in the assembly.

The type name (second parameter) must be the fully qualified name, not just the class name.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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