Activator.CreateInstance() troubles

2019-05-10 15:28发布

I have a factory that is supposed to create objects that inherit from class Foo at run-time. I would think that System.Activator.CreateInstance's return type was the same as the type of an object it's creating, but judging from the following error message, its return type is Object.

Error 1 Cannot implicitly convert type 'object' to 'cs_sandbox.Foo'. An explicit conversion exists (are you missing a cast?) F:\projects\cs_sandbox\Form1.cs 46 24 cs_sandbox

OK, so maybe I am missing a cast, but

return (t)System.Activator.CreateInstance(t);

results in yet another error message, which -- I must admit -- makes no sense to me:

Error 1 The type or namespace name 't' could not be found (are you missing a using directive or an assembly reference?) F:\projects\cs_sandbox\Form1.cs 45 25 cs_sandbox

And here's my code:

class Foo { }
class FooChild1 : Foo { }
class FooChild2 : Foo { }

class MyFactory
{
    public static Foo CreateInstance(string s)
    {
        Type t;
        if (s.StartsWith("abcdef"))
        {
            t = typeof(FooChild1);
            return System.Activator.CreateInstance(t);
        }
        else
        {
            t = typeof(FooChild2);
            return System.Activator.CreateInstance(t);
        }
    }
}

How can I fix this code? Or, if it's not fixable, what are other ways of creating objects that inherit from a specific class at run-time?

6条回答
孤傲高冷的网名
2楼-- · 2019-05-10 16:05

I think the correct is:

public static Foo CreateInstance(string objectIdentifer)
{
   if (objectIdentifier == "Child1")
   {
      return (Foo)Activator.CreateInstance(Type.GetType("Foo.FooChild1, FooChild1"));
   }
   else
   {
      return (Foo)Activator.CreateInstance(Type.GetType("Foo.FooChild1, FooChild2"));
   }
}

Hope that can help you

查看更多
闹够了就滚
3楼-- · 2019-05-10 16:09

Maybe a little late but if i don't know the type (in case you build your own codeparser) I use Convert.ChangeType

  string original = "1";
  object value;
  value = Convert.ChangeType(original, typeof(int));

typeof(int) can be any type you don't know by using object.GetType();

查看更多
贼婆χ
4楼-- · 2019-05-10 16:11

I think you should probably do this:

public static Foo CreateInstance(string objectIdentifer)
{
   if (objectIdentifier == "Child1")
   {
      return (Foo)Activator.CreateInstance("Foo.FooChild1, FooChild1");
   }
   else
   {
      return (Foo)Activator.CreateInstance("Foo.FooChild1, FooChild2");
   }
}

My point is that in this code, the CreateInstance method has no direct reference to the assembly or assemblies containing FooChild1 and FooChild2. In the original code, you create a type by explicitly naming FooChild1 and FooChild2, so you might as well just new them instead of activating them.

Does this make sense to you?

查看更多
劳资没心,怎么记你
5楼-- · 2019-05-10 16:12

I had to use to UnWrap() method after activating, like described on MSDN:

// Creates an instance of MyType defined in the assembly called ObjectHandleAssembly.
ObjectHandle obj = domain.CreateInstance("ObjectHandleAssembly", "MyType");

// Unwrapps the proxy to the MyType object created in the other AppDomain.
MyType testObj = (MyType)obj.Unwrap();

As described on MSDN. Furthermore, I had to use parameters like this:

    ObjectHandle obj = domain.CreateInstance("Other.Assembly.Name", "Full.Class.Namespace.ClassName");
查看更多
我只想做你的唯一
6楼-- · 2019-05-10 16:16

You need to cast the returned object to Foo type. It doesn't make sense to cast it to a type defined in a variable. It should be known by the compiler, as the whole point of casting through the inheritance hierarchy is satisfying compiler's static type checking.

return (Foo)System.Activator.CreateInstance(t);

There's a generic version, System.Activator.CreateInstance<T>, which creates a known type (not a type variable but a type argument or a statically known type, in the latter case, it doesn't make much sense though):

return System.Activator.CreateInstance<FooChild1>();
查看更多
欢心
7楼-- · 2019-05-10 16:18

After you changed code

Just cast your results to Foo like

return System.Activator.CreateInstance(t) as Foo;

or

return (Foo)System.Activator.CreateInstance(t);

The first one is a bit more robust because you won't get an exception in case cast is not possible. It will simply return null. But it depends what you want.

Before you changed code

Have you tried generics?

public static OutType CreateInstance<OutType>(string s)
{
    Type t;
    if (s.StartsWith("abcdef"))
    {
        t = typeof(Bar);
        return System.Activator.CreateInstance(t) as OutType;
    }
    else
    {
        t = typeof(Meh);
        return System.Activator.CreateInstance(t) as OutType;
    }
}

But you do have a problem. In your existing static method you are trying to return Bar and Meh that aren't related to Foo in any way. This will always be an exception unless your method also returns an object or a common ancestor type (as in casting).

To control even more internal types you could define more than one generic type that your method would use internally.

查看更多
登录 后发表回答