- Home /
How can I invoke a Method Property from a MethodInfo (Reflection) while keeping it's properties and types generic?
public class MyComponent{
void MyBoolClass(bool value){};
void MyFloatClass(float value){};
float MyReturnFloat(){return 1f;};
}
private Action myAction = ()=>{};
public MyComponent myComponent;
void Start()
{
MethodInfo methods = myComponent.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
//I will want to create a list later, just using the first index as a code example
myAction = (Action)Delegate.CreateDelegate(typeof(Action), this, methods[0]);
//Currently getting an Exeption
}
I am trying to invoke a method from an array of methods but I running into a problem, how would I go about using dynamic generic properties? In the example how am I able to create an action to handle the MyBoolClass and MyFloatClass?
Thanks guys!
Answer by Bunny83 · Feb 25, 2017 at 12:16 AM
Your question doesn't make much sense. You have a delegate type that takes no parameters and has no return type. All your methods either have a parameter or return a value. Those methods have completely incompatible signatures. There is no "generic" delegate type that can be directly used. A generic type like Action<T>
requires a type argument Action
and Action<T>
are also completely incompatible to each other.
What exactly are you trying to achieve? If you want "somehow" assign "MyBoolClass" or "MyFloatClass" to an Action
delegate you would need a closure that includes the parameter.
For example you would need a class like this:
public class BoolClosure
{
public bool parameter;
public System.Action<bool> method;
public BoolClosure(MethodInfo aMethod, object aObj, bool aParameter)
{
var t = typeof(System.Action<bool>);
method = (System.Action<bool>)System.Delegate.CreateDelegate(t, aObj, aMethod);
}
public void Execute()
{
method(parameter);
}
}
Usually closure classes are generated by the compiler automatically. However since you want to create them dynamically you have to create them yourself.
You can use it like this:
MethodInfo method = myComponent.GetType().GetMethod("MyBoolClass", BindingFlags.Instance | BindingFlags.NonPublic);
myAction = new BoolClosure(method, myComponent, true).Execute;
Now when you invoke myAction like this:
myAction();
You will actually invoke the method "MyBoolClass" with the parameter "true". What we just manually created actually happens when you do something like:
myAction = ()=>myComponent.MyBoolClass( true );
Though we still don't know what you actually want to do. You should add more information on your background. For what purpose would you need this?
Hey bunny thanks for the reply, as for my question I am asking how I can achieve this, I understand my delegate takes in no parameters and has no return type that is why I started this question.
As for the information I am trying to invoke methods (On unity components) dynamically through reflection. Example: Light component contains [Intensity] that accepts a float, I need to create a generic action to pass floats, Light also contains set active that accepts a bool, I need to create a generic action to pass a bool.
I am using reflection to populate an array of methods (I don’t fully understand why you’re telling me to use .get$$anonymous$$ethod(), I’m not using get$$anonymous$$ethod(), I am using get$$anonymous$$ethods(), that returns an array of methods, this is where the dynamic part comes in): $$anonymous$$ethodInfo methods = myComponent.GetType().Get$$anonymous$$ethods()
from that array I want to be able to invoke the methods that are in the component class that I assigned to myComponent, I need the missing explanation as to how I can invoke [methods] in this case dynamically since I don’t know how I can specify a generic return type or parameter, and I don’t know if it’s possible or not.
$$anonymous$$y guess is something like to put it in quasi pseudo code: myAction = (Action<T>)Delegate.CreateDelegate(typeof(Action<t>), this, methods[i]);
Thanks again, let me know if this makes sense.
As i said you can not have a delegate variable that can hold different delegate types. .NET / $$anonymous$$ono is a strongly typed environment. So you can't treat completely different methods in a generic manner. The only exception is reflection. So store your $$anonymous$$ethodInfo and the object reference which contains the method. You can always directly invoke a $$anonymous$$ethodInfo
methods[0].Invoke(myComponent, new object[] { true });
Instance methods require the object instance as first parameter. All actual method parameters has be be passed to Invoke as an object array. In this case i pass "true" to the method. $$anonymous$$eep in $$anonymous$$d whenever the type you pass doesn't match the type of the method signature your code will throw an exception and is ter$$anonymous$$ated.
btw: I used Get$$anonymous$$ethod because i wanted to post a working example. You for example didn't declare your "methods" variable as array. Also my example only works when the signature matches. You can't create a delegate in a generic manner. A delegate variable has to have a distinct type at runtime.
You may not fully understand what a generic type actually is. It doesn't mean it can simply take a dynamic type. It's just a way to avoid having to write the same code several times. To actually use a generic type you have to create a "concrete type" out of the generic type in order to use it. Action<bool>
is a concrete type. Action<>
is the generic type. Again, the generic type can't be used at runtime. It can only be used with reflection to construct a concrete type ($$anonymous$$akeGeneric$$anonymous$$ethod / $$anonymous$$akeGenericType).
You still haven't sait how you actually want to use this. Again storing completely different things in an array makes no sense since they can't be treated similar as they aren't similar at all.
ps: I just had another look at your question. The "this" in this line makes no sense at all:
myAction = (Action)Delegate.CreateDelegate(typeof(Action), this, methods[0]);
You have to pass the object instance reference to CreateDelegate. So the object that actually contains that method which is in your case your "myComponent" object and not the object that contains your Start method.
Answer by hrgchris · Feb 25, 2017 at 07:26 AM
Hi there
Instead of trying to store an action created by CreateDelegate (which is strongly typed), store the MethodInfo. Then use MethodInfo.Invoke to call the method, which just takes a set of objects as arguments and should do the conversions/ type checking internally.
Generally the CreateDelegate is there as a tool for when you specifically need to get a way of calling the code without reflection such as attaching it to an event, or passing into other code that doesn't handle reflection. As you can imagine, this naturally imposes the strongly typed properties of c#, so at that point you lose the ability to ignore types.
Chris
Within .NET / $$anonymous$$ono you can never "ignore types", even when using reflection. If a method requires a string parameter you have to pass a string as argument. You can / have to do it in a dynamic way, but it doesn't provide any more flexibility. It just adds more potential for mistakes which lead to runtime errors. Invoke doesn't do any type conversion. If you feed the wrong parameter types it will throw an exception. That includes types that are usually implicitly converted in C# code.
A common example would be valuetypes. Everybody knows you can cast an int into a float (even implicitly). Though when dealing with value types in a "more generic way" (storing it as "object") you can't directly cast an int into a float. Example:
int i = 5;
object o = i;
float f = (float)o; // error here
float f = (int)o; // this works
Likewise the other way round
float f = 5f;
object o = f;
int i = (int)o; // error here
int i = (int)(float)o; // this works
You would have use the System.Convert
class to directly convert a boxed value type into another type:
int i = System.Convert.ToInt32(o);
Though the Convert class can also only do any conversions that you can do with the actual types. The most "generic" way when dealing with reflection would be System.Convert.ChangeType
which can convert an object value into another type that is provided as System.Type. But again, it can only convert things that are actually convertable.
That means if you plan to use reflection to get a list of unknown methods you would need to do a lot of analyzing of it's parameter types in order to actually invoke it. Generic methods (like GetComponent<T>
) will give you even more headache as you first have to construct a non-generic methodinfo out of the generic one by providing the generic type parameter info.
And that's why i ask for what exact purpose he needs this. It simply makes no sense to treat arbitrary methods with completely different signatures in a generic way because you can not simply "call them".
Ummm.. Ok. Not sure I disagreed with any of that, but hey ho.
Like I say, if you want to avoid storing bespoke delegates for every potential argument, store the method infos and use Invoke. my few cents.
Yes, that's what i already mentioned in my comment above 14 hours ago ^^.
Your answer
Follow this Question
Related Questions
How do I get the system type of Mathf in JS ? 1 Answer
Finding a method by custom attribute and passing to a UI button 1 Answer
Casting methods, events, and/or delegates from strings / enum 0 Answers
EventManager with parameters 1 Answer
Reflection - passing parameters to an invoked function 2 Answers