Problem with array initialization using Activator.CreateInstance

T

Terry

I'm building some dll assemblies that have in them the implementation of an
abstract class defined in a different assembly.

I'm trying to create objects of the type defined in the dlls with
"Activator.CreateInstance".

Everything was working fine until I started to fill out the class def with
some implementation. At some point the CreateInstance was failing with a
"MissingMethodException - No parameterless constructor defined for this
object".

I tracked down the problem to an array I was creating in the implementation
of one of the methods.

Specifically, if I create my array like this:

byte [] myByteArray = {0xFF, 0xFF, 0xFF};

it will throw an exception. But, if I create it like this:

byte [] myByteArray = new byte[3];
myByteArray[0] = 0xFF;
etc.

It works fine.

I'm a bit puzzled by this. How can all function local code affect the
creation of an object? And why would it make a difference how the array is
created and initialized within a method of the derived class?

Can anyone help clear this up? Am I doing something I shouldn't be?

Thanks!
 
J

Jon Skeet

Terry said:
I'm a bit puzzled by this. How can all function local code affect the
creation of an object? And why would it make a difference how the array is
created and initialized within a method of the derived class?

Can anyone help clear this up? Am I doing something I shouldn't be?

<snip>

That sounds very odd to me. Could you provide a short but complete
example which demonstrates the problem?
See http://www.pobox.com/~skeet/csharp/complete.html for what I mean by
that.
 
T

Terry

Ok. Here it is. Here's a simple test case which demonstrates the problem
I'm having. If I'm doing something wrong, please let me know.

There are 3 projects created. One is the "interface dll" which contains the
abstract class. Another is the "implementation dll" which contains a class
that derives from and implements the abstract class. And there's a Windows
forms app to use as the driver. Here are the steps and code.

1. Create a "Class library" project. This will define an abstract class to
act as an interface. Call it "InterfaceDll".

Here is the code for the source file in this project. Compile it.

using System;
namespace InterfaceDll
{
/// <summary>
/// Summary description for Class1.
/// </summary>
public abstract class TheInterface
{
public abstract int DoSomething1();
public abstract int DoSomething2();
}
}

2. Create another "Class library" project. Call it "ImplementationDll".
Add a project reference to the built "InterfaceDll" you created in step one.
Here is the source for this one.

using System;
namespace ImplementationDll
{
/// <summary>
/// Summary description for Class1.
/// </summary>
public class Implementation : InterfaceDll.TheInterface
{
public Implementation()
{
}
public override int DoSomething1()
{
//byte[] myBytes = {0xFF, 0xFF, 0xFF, 0xFF};
//foreach (byte b in myBytes)
//{
// Console.Write(b.ToString() );
//}
return 0;
}
public override int DoSomething2()
{
return 0;
}
}
}

* Note the lines commented out. Leave them commented out for now. Build
this dll.

3. Create a Windows forms app with a single button on the form. Add a
reference to the "InterfaceDll.dll" built in step 1 to this project. Add a
click handler for the button. Add the following code to the button handler.

string strCurDir = Environment.CurrentDirectory;
string filename = Path.Combine(strCurDir, "implementationdll.dll" );
//Load the Assembly
Assembly a = Assembly.LoadFrom(filename);
// get all the types in the loaded assembly
Type[] types = a.GetTypes();
foreach (Type typ in types)
{
// dynamically create or activate(if exist) object
object obj = Activator.CreateInstance(typ);
}

Build the forms app. Now, copy the "ImplementationDll.dll" from step 2 into
the "\bin\Debug" directory so the code can locate the assembly.

4. Run the windows forms app. Click the button. Note how it runs
properly.

5. Now, uncomment the code in the "DoSomething1" method in the
"ImplementationDll" project, build it and (DON'T FORGET) copy the new dll
into the same location as in step 3.

6. Run the windows forms app again. Click the button. It will throw an
exception on the "object obj = Activator.CreateInstance(typ);" line.

"An unhandled exception of type 'System.MissingMethodException'
occurred in mscorlib.dll
Additional information: No parameterless constructor defined for
this object."

If you change the byte[] array allocation to use "new" instead of an
initializer list like the following, it works ok.

byte[] myBytes = new byte[4];
myBytes[0] = myBytes[1] = myBytes[2] = myBytes[3] = 0xFF;

Why does using an initializer for the byte[] array in the "DoSomething1"
method cause an error? Am I doing something wrong?

Thanks!

Terry
 
J

Jon Skeet

Terry said:
Ok. Here it is. Here's a simple test case which demonstrates the problem
I'm having. If I'm doing something wrong, please let me know.

<snip>

Brief request - I reckon it's generally easier to do this kind of thing
with console apps. In fact, you don't even need the three different
classes here. Here's a short class which demonstrates everything:

using System;
using System.Reflection;

class Test
{
public static void Main()
{
try
{
foreach (Type t in
Assembly.GetExecutingAssembly().GetTypes())
{
Console.WriteLine (t.Name);
Activator.CreateInstance (t);
}
}
catch (Exception e)
{
Console.WriteLine (e);
}
}

public void Foo()
{
byte[] myBytes = {0xFF, 0xFF, 0xFF, 0xFF};
}
}


Even with the three-class system a console app would end up being
shorter to demonstrate with. Anyway, on with the problem itself.


You're iterating over *all* the types within the assembly - and the one
which is failing (at least on my system) is a type called:
<PrivateImplementationDetails> (including the angle brackets).

If you compile the console app above and run it, you'll see the same
thing. Comment out the line in Foo and it runs fine. Comment out the
line using Activator.CreateInstance instead, and you'll get something
like:

Test
<PrivateImplementationDetails>
$$struct0x6000002-1

Now, using Reflector (http://www.aisto.com/roeder/dotnet/) or ildasm
you can have a look and see that the struct is nested within
<PrivateImplementationDetails>. I believe it basically contains the
initialisation data for the byte array. Add some more arrays and you'll
see the same thing, growing and growing. Look at mscorlib and there are
loads of them.

The upshot of this is that you should basically take more care about
which types you want to create instances of, although I agree it's a
somewhat surprising case. If you only look at public types (using
Type.IsPublic) you should be fine.
 
T

Terry

Ah. I see. Thank you very much! You're a credit to the Internet! :)

Terry

Jon Skeet said:
Terry said:
Ok. Here it is. Here's a simple test case which demonstrates the problem
I'm having. If I'm doing something wrong, please let me know.

<snip>

Brief request - I reckon it's generally easier to do this kind of thing
with console apps. In fact, you don't even need the three different
classes here. Here's a short class which demonstrates everything:

using System;
using System.Reflection;

class Test
{
public static void Main()
{
try
{
foreach (Type t in
Assembly.GetExecutingAssembly().GetTypes())
{
Console.WriteLine (t.Name);
Activator.CreateInstance (t);
}
}
catch (Exception e)
{
Console.WriteLine (e);
}
}

public void Foo()
{
byte[] myBytes = {0xFF, 0xFF, 0xFF, 0xFF};
}
}


Even with the three-class system a console app would end up being
shorter to demonstrate with. Anyway, on with the problem itself.


You're iterating over *all* the types within the assembly - and the one
which is failing (at least on my system) is a type called:
<PrivateImplementationDetails> (including the angle brackets).

If you compile the console app above and run it, you'll see the same
thing. Comment out the line in Foo and it runs fine. Comment out the
line using Activator.CreateInstance instead, and you'll get something
like:

Test
<PrivateImplementationDetails>
$$struct0x6000002-1

Now, using Reflector (http://www.aisto.com/roeder/dotnet/) or ildasm
you can have a look and see that the struct is nested within
<PrivateImplementationDetails>. I believe it basically contains the
initialisation data for the byte array. Add some more arrays and you'll
see the same thing, growing and growing. Look at mscorlib and there are
loads of them.

The upshot of this is that you should basically take more care about
which types you want to create instances of, although I agree it's a
somewhat surprising case. If you only look at public types (using
Type.IsPublic) you should be fine.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top