0
\$\begingroup\$

Let's say that I have a component like this:

public class MyComponent: MonoBehaviour {
    public void MyMethod(int param1, int param2) {
        Debug.Log(param1 + " " + param2);
    }
}

And I have a string variable that has method name from that class.

string methodName = "MyMethod";

I need to invoke MyComponent.MyMethod with reflection api, however I don't know how to distinguish component that has "MyMethod".

MonoBehaviour[] scripts = target.GetComponents<MonoBehaviour>();

// Find the component that has method
for(int j = 0; j < scripts.Length; j++) {
    ... ??? ...
}

methodInfo.Invoke(target.gameObject, parameters);
hasFound = true;

If I just invoke with gameObject, of course it fails. So I have to get the component to use Invoke. In this case, how do I get the component that has specified method by Reflection?

\$\endgroup\$
  • \$\begingroup\$ This is a bit of an unusual thing to need to do. Typically, a data source that knows enough about your source code to specify the exact method name would also know enough to specify what types it might be found in. Can you clarify what the broader feature you're trying to implement with this might be? I'm getting an X/Y problem vibe that suggests there might be a cleaner way to reach that goal. \$\endgroup\$ – DMGregory Jul 11 '18 at 17:04
  • \$\begingroup\$ @DMGregory I'm implementing something like RPC. After game begins, Game will find the all methods that marked as RPC attribute and collect them and save it. After one of client calls RPC, it sends method name with parameter to other clients, and other clients will execute the method with parameter extracted from the data that received from RPC. \$\endgroup\$ – modernator Jul 11 '18 at 17:16
  • \$\begingroup\$ @DMGregory I can access the object that have the component, but unfortunately I can't find the way to get that component so that I can invoke it. \$\endgroup\$ – modernator Jul 11 '18 at 17:18
  • \$\begingroup\$ So... what you need is a switch(string) and what you want is dynamic exploring of all methods on all scripts? \$\endgroup\$ – wondra Jul 11 '18 at 19:05
  • \$\begingroup\$ @wondra No, I don't want to explore all of them unless there is no alternative. I just want to get component that has method with specified name from the gameObject that already have that component. \$\endgroup\$ – modernator Jul 11 '18 at 19:15
1
\$\begingroup\$

If you are looking for a certain method by a specific name, why don't you use interfaces instead? That way you can make any MonoBehaviour class implement that interface. Of course, this wouldn't let you make use of custom attributes (like RPC) for you to state a method to call by its name. But it would solve the problem (although not with your desired approach).

public interface ICustomInterface
{
    void MyMethod(int indexArg);
}

public class MonoA : MonoBehaviour, ICustomInterface
{
    public void MyMethod(int indexArg)
    {
        Debug.Log("I am doing what a MonoA ought to do with the Index: " + indexArg.ToString());
    }
}

public class MonoB : MonoBehaviour, ICustomInterface
{
    public void MyMethod(int indexArg)
    {
        Debug.Log("I am doing what a MonoB ought to do with the Index: " + indexArg.ToString());
    }
}

public class Example : MonoBehaviour
{
    public GameObject target;

    void Start()
    {
        ICustomInterface[] scripts = target.GetComponents<ICustomInterface>();

        for(int j = 0; j < scripts.Length; j++) {
            scripts[j].MyMethod(j);
        }

        hasFound = (scripts.Length > 0);
    }
}
\$\endgroup\$
0
\$\begingroup\$

Before I post any pseudo-code, I do entirely agree with what @DMGregory has posted. If your code knows enough to know the method, there's probably a cleaner way to do it. Interfaces are a nice and clean approach (as posted by @LinkWindcrafter in another answer here).

Now, if you've explored all reasonable options and there's simply no other alternative... I think you may be looking for the Type.GetMethod(String, BindingFlags) feature.

Here's some quickly hacked together pseudo-code:

    public struct OwnerAndMethod
    {
        public object Owner { get; private set; }

        public MethodInfo Method { get; private set; }

        public void Invoke ()
        {
            Method.Invoke (Owner, null);
        }

        public OwnerAndMethod (object owner, MethodInfo method) : this ()
        {
            Owner = owner;
            Method = method;
        }
    }


    private static List<OwnerAndMethod> GetMethodsFromComponentsViaReflection (IList<object> objects, string methodName)
    {
        List<OwnerAndMethod> ownerAndMethods = new List<OwnerAndMethod> ();
        for (int i = 0; i < objects.Count; i++)
        {
            MethodInfo method = objects[i].GetType ().GetMethod (methodName, BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);
            if (method != null)
            {
                ownerAndMethods.Add (new OwnerAndMethod (objects[i], method));
            }
        }

        return ownerAndMethods;
    }

    private void Start ()
    {
        MonoBehaviour[] components = transform.GetComponents<MonoBehaviour> ();
        List<OwnerAndMethod> methods = GetMethodsFromComponentsViaReflection (components, "Hey");

        foreach (OwnerAndMethod method in methods)
        {
            method.Invoke ();
        }
    }     method.Invoke ();
        }
    }

There's obviously so many possible exceptions that can be thrown from this code, so take this as a reference and not so much an exact way of doing it.

I hope this helps. Good luck!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.