I am trying to instantiate a type using Activator's reflection magic. Unfortunately the type I want to instantiate has a params parameter of type object. See this extract:
public class Foo
{
public Foo(string name, params object[] arguments)
{
}
}
And the instantiation goes here:
public static class Bar
{
public static object Create(params object[] arguments)
{
return Activator.CreateInstance(typeof(Foo), "foo", arguments);
}
}
Now, this effectively results in a constructor call with the signature
new Foo(string, object[])
because object[] is also object.
What I actually want is:
new Foo(string, object, object, object, ...)
Is this even possible with Activator? Or how do I instantiate a type with such a parameter type?
params is a purely-compile-time syntactic sugar.
The runtime, including the parameter binding used by reflection, ignores it.
You need to pass a normal array, just like a non-params parameter.
In your case, it sounds like you're trying to not call the params overload.
You need to build a single (flattened) array containing all of the parameters you want to pass:
object[] args = new object[arguments.Length + 1];
args[0] = "foo";
arguments.CopyTo(args, 1);
You can pass parameters like this:
return Activator.CreateInstance(
typeof(Foo),
new object[] { "foo" }.Concat(arguments).ToArray());
Related
Consider this code sample from a WinForms app:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
object[] parms = new object[1];
parms[0] = "foo";
DoSomething(parms);
}
public static string DoSomething(object[] parms)
{
Console.WriteLine("Something good happened");
return null;
}
}
It works as expected, when you click button1 it prints "Something good happened" to the console.
Now consider this code sample, which is the same except that it invokes DoSomething using reflection:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
object[] parms = new object[1];
parms[0] = "foo";
System.Reflection.MethodInfo mi = typeof(Form1).GetMethod("DoSomething");
mi.Invoke(null, parms);
}
public static string DoSomething(object[] parms)
{
Console.WriteLine("Something good happened");
return null;
}
}
It throws an System.ArgumentException on the line mi.Invoke(null, parms) (Object of type 'System.String' cannot be converted to type 'System.Object[]'.)
parms is clearly an object array, and DoSomething's method signature is clearly expecting an object array. So why is invoke pulling the first object out of the array and trying to pass that instead?
Or is something else going on that I'm not understanding?
MethodInfo.Invoke is expecting an object array, where each object in the object array corresponds to an argument to the method. The first argument in the object array is the first argument, the second object in the array the second method, etc.
Since you want the first argument to your method to be an object[], you need to ensure that the first object in the object array you pass to MethodInfo.Invoke is an object array that represents the array that DoSomething should use.
object[] parms = new object[1];
parms[0] = "foo";
with:
public static string DoSomething(object[] parms)
that's the problem; the first parameter is not a string - it is an object[]. The object[] that you pass to Invoke represents each parameter in turn, so an object[] of length 1 with a string would match static string DoSomething(string s), but does not match your method. Either change the signature, or wrap the value. Frankly, I suggest changing the signature is the better idea here, but:
parms[0] = new object[] { "foo" };
would also work
MethodInfo.Invoke expects an array of objects as parameter to pass multiple arguments to the functions, each object in the array will be a different parameter.
As your function also expects an object array you are passing as argument not an object array but an string.
You must wrap that array into another array, in this way the Invoke will unwrap the first array and use the inner array as the first parameter for the call.
mi.Invoke(null, new object[]{ parms });
parms is clearly an object array, and DoSomething's method signature is clearly expecting an object array.
Yes it is expecting an object array. But when you pass this:
object[] parms = new object[1];
You are saying these are the arguments for DoSomething so parms[0] is the first argument to DoSomething and if there were more items in parms then parms[1] will be the 2nd argument and so on.
Clearly the first argument for DoSomething is not a string (parms[0]) so you get this error:
It throws an System.ArgumentException on the line mi.Invoke(null, parms) (Object of type 'System.String' cannot be converted to type 'System.Object[]'.)
How do I call SomeObject.SomeGenericInstanceMethod<T>(T arg) ?
There are a few posts about calling generic methods, but not quite like this one. The problem is that the method argument parameter is constrained to the generic parameter.
I know that if the signature were instead
SomeObject.SomeGenericInstanceMethod<T>(string arg)
then I could get the MethodInfo with
typeof (SomeObject).GetMethod("SomeGenericInstanceMethod", new Type[]{typeof (string)}).MakeGenericMethod(typeof(GenericParameter))
So, How do I go about getting the MethodInfo when the regular arguments are of a generic type? Thanks!
Also, there may or may not be type constrains on the generic parameter.
You do it exactly the same way.
When you call MethodInfo.Invoke, you pass all the arguments in an object[] anyway, so it's not like you have to know the types at compile time.
Sample:
using System;
using System.Reflection;
class Test
{
public static void Foo<T>(T item)
{
Console.WriteLine("{0}: {1}", typeof(T), item);
}
static void CallByReflection(string name, Type typeArg,
object value)
{
// Just for simplicity, assume it's public etc
MethodInfo method = typeof(Test).GetMethod(name);
MethodInfo generic = method.MakeGenericMethod(typeArg);
generic.Invoke(null, new object[] { value });
}
static void Main()
{
CallByReflection("Foo", typeof(object), "actually a string");
CallByReflection("Foo", typeof(string), "still a string");
// This would throw an exception
// CallByReflection("Foo", typeof(int), "oops");
}
}
You do it exactly the same way, but pass an instance of your object:
typeof (SomeObject).GetMethod(
"SomeGenericInstanceMethod",
yourObject.GetType())
// Or typeof(TheClass),
// or typeof(T) if you're in a generic method
.MakeGenericMethod(typeof(GenericParameter))
The MakeGenericMethod method only requires you to specify the generic type parameters, not the method's arguments.
You'd pass the arguments in later, when you call the method. However, at this point, they're passing as object, so it again doesn't matter.
I have a class which contains an empty constructor and one that accepts an array of objects as its only parameter. Something like...
public myClass(){ return; }
public myClass(object[] aObj){ return; }
This is the CreateInstance() method call that I use
object[] objectArray = new object[5];
// Populate objectArray variable
Activator.CreateInstance(typeof(myClass), objectArray);
it throws System.MissingMethodException with an added message that reads
"Constructor on type 'myClass' not found"
The bit of research that I have done has always shown the method as being called
Activator.CreateInstance(typeof(myClass), arg1, arg2);
Where arg1 and arg2 are types (string, int, bool) and not generic objects.
How would I call this method with only the array of objects as its parameter list?
Note: I have tried adding another variable to the method signature. Something like...
public myClass(object[] aObj, bool notUsed){ return; }
and with this the code executed fine.
I have also seen methods using reflection which were appropriate but I am particularly interested in this specific case. Why is this exception raised if the method signature does in fact match the passed parameters?
Cast it to object:
Activator.CreateInstance(yourType, (object) yourArray);
Let's say you have constructor:
class YourType {
public YourType(int[] numbers) {
...
}
}
I believe you would activate like so by nesting your array, the intended parameter, as a item of the params array:
int[] yourArray = new int[] { 1, 2, 4 };
Activator.CreateInstance(typeof(YourType ), new object[] { yourArray });
How do I call SomeObject.SomeGenericInstanceMethod<T>(T arg) ?
There are a few posts about calling generic methods, but not quite like this one. The problem is that the method argument parameter is constrained to the generic parameter.
I know that if the signature were instead
SomeObject.SomeGenericInstanceMethod<T>(string arg)
then I could get the MethodInfo with
typeof (SomeObject).GetMethod("SomeGenericInstanceMethod", new Type[]{typeof (string)}).MakeGenericMethod(typeof(GenericParameter))
So, How do I go about getting the MethodInfo when the regular arguments are of a generic type? Thanks!
Also, there may or may not be type constrains on the generic parameter.
You do it exactly the same way.
When you call MethodInfo.Invoke, you pass all the arguments in an object[] anyway, so it's not like you have to know the types at compile time.
Sample:
using System;
using System.Reflection;
class Test
{
public static void Foo<T>(T item)
{
Console.WriteLine("{0}: {1}", typeof(T), item);
}
static void CallByReflection(string name, Type typeArg,
object value)
{
// Just for simplicity, assume it's public etc
MethodInfo method = typeof(Test).GetMethod(name);
MethodInfo generic = method.MakeGenericMethod(typeArg);
generic.Invoke(null, new object[] { value });
}
static void Main()
{
CallByReflection("Foo", typeof(object), "actually a string");
CallByReflection("Foo", typeof(string), "still a string");
// This would throw an exception
// CallByReflection("Foo", typeof(int), "oops");
}
}
You do it exactly the same way, but pass an instance of your object:
typeof (SomeObject).GetMethod(
"SomeGenericInstanceMethod",
yourObject.GetType())
// Or typeof(TheClass),
// or typeof(T) if you're in a generic method
.MakeGenericMethod(typeof(GenericParameter))
The MakeGenericMethod method only requires you to specify the generic type parameters, not the method's arguments.
You'd pass the arguments in later, when you call the method. However, at this point, they're passing as object, so it again doesn't matter.
Given a null cast:
var result = MyMethod( (Foo) null );
Is it possible to use this extra information inside the method with reflection?
EDIT:
The method's signature is something like:
object MyMethod( params object[] args )
{
// here I would like to see that args[0] is (was) of type Foo
}
Ahh... you edited...
I suspect the closest you'll get is generics:
object MyMethod<T>( params T[] args ) {...}
(and look at typeof(T))
But that assumes all the args are the same. Other than that; no. Every null is the same as every other (Nullable<T> aside), and you cannot tell the variable type.
Original reply:
Do you mean overload resolution?
object result = someType.GetMethod("MyMethod",
new Type[] { typeof(Foo) })
.Invoke(someInstance, new object[] { null });
(where someInstance is null for static methods, and someType is the Type that has the MyMethod method)
Short answer: No
I'm guessing you got something like this:
class Foo : Bar{}
Since you've got:
object MyMethod(param object[] values);
There's no way to do this. You could use a null object pattern to accomplish this:
class Foo : Bar
{
public static readonly Foo Null=new Foo();
}
and then call with Foo.Null instead of null. Your MyMethod could then check for the static instance and act accordingly:
object MyMethod(param object[] values
{
if(values[0]==Foo.Null) ......
}