2

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 AbstractTypeclass 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?

Community
  • 1
  • 1
Arcadian
  • 1,373
  • 4
  • 20
  • 30

3 Answers3

8

If you have the fully qualified name of your type you don't need this foreach loop at all:

var myObject = (SomeBaseType) Activator.CreateInstance(null, myTypeName);
BrokenGlass
  • 158,293
  • 28
  • 286
  • 335
  • When I try this I get an error saying that the parameter for AssignInitialValues is less accessible than the method itself. I don't see how that can be true since the parameter is just a class (which can't be private). – Arcadian Sep 15 '12 at 00:26
6

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[]{})
CodeTherapist
  • 2,776
  • 14
  • 24
  • Using your more efficient answer gets me a runtime exception: nullReferenceException. I believe, that like the other answer, this is caused by the code looking in the executing assembly and not `MyAssembly`. – Arcadian Sep 16 '12 at 00:25
4

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.

Arcadian
  • 1,373
  • 4
  • 20
  • 30
  • When you refer to a type through a string, you must always use the fully qualified name (ie with namespace), and if the type is not in the assembly where the method is running (or in the executing assembly?), you must also specify the assembly name. So basically, you should always provide the assembly qualified name (see `Type.AssemblyQualifiedName`) when doing Activator.CreateInstance or Type.GetType. Else .Net won't be able to find your type. – user276648 Dec 03 '12 at 02:43