Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Can Reflection and Invocation be used in WebPlayer?

Discussion in 'Scripting' started by RedVonix, Apr 28, 2014.

  1. RedVonix

    RedVonix

    Joined:
    Dec 13, 2011
    Posts:
    414
    Howdy team,

    I've got a pretty cool dialog flow system that I wrote for a project. To keep things open and flexible, I used reflection throughout it. Now, the client wants to see if things will work in the WebPlayer - so I load it up, and immediately the dialog flow system craps out with this error:

    Did a little research and it sounds like this should work, based on this line from Unity's documentation:
    I did indeed write the methods myself, and they are not private classes... at least I don't think so. Maybe you guys can give some advice. This here is the block of code that it's dying on:

    Code (csharp):
    1.  
    2.     var newDialogObj = new object();
    3.     getDialogScriptForDialog<System.Type>(thisDialog, out newDialogObj);
    4.            
    5.     Type type = newDialogObj.GetType();
    6.     var method = type.GetMethod(methodString);
    7.     method.Invoke(newDialogObj, inObj); // This line specifically is where it dies
    8.  
    Any help or pointers would be very appreciated - thanks in advance!
     
  2. RedVonix

    RedVonix

    Joined:
    Dec 13, 2011
    Posts:
    414
    Bump.

    Also, doing some research into it, it looks like it's not specifically the .Invoke that is causing the failure - it's actually the parameters. I experimented by doing a .SendMessage into an object instead, and while that worked - it hated the idea of figuring out what the parameter types were. Which means the following code works fine on all platforms, but will not work on WebPlayer:

    Code (csharp):
    1.  
    2. void sendMessageToObj(GameObject target, string Message, object variable)
    3. {
    4.   target.SendMessage(Message, variable);
    5. }
    6.  
    So the question is - how do you make the above code work on WebPlayer? What needs to be modified so that the conversion between 'object' and its correct Type will occur? I've tried the following but it doesn't work:

    Code (csharp):
    1.  
    2.   target.SendMessage(Message, (typeof(variable.GetType()))variable);
    3.  
     
  3. KheltonHeadley

    KheltonHeadley

    Joined:
    Oct 19, 2010
    Posts:
    1,685
    You're doing it wrong.
     
    Deleted User likes this.
  4. Glader

    Glader

    Joined:
    Aug 19, 2013
    Posts:
    449
    Reference this MSDN page http://msdn.microsoft.com/en-us/lib...?cs-save-lang=1&cs-lang=csharp#code-snippet-1

    If you're trying to invoke on a method info in which you don't know the parameter list type you might be able to use reflection again to discover this at runtime but that just seems like bad design.

    Edit: The first arg is not the Type instance. You need to give it an instance of the Type that you peeled the method info from. Otherwise it needs to be a static method. Essentially the invoke on the MethodInfo needs an instance of the object you're trying to invoke the method from.
     
    Last edited: Apr 29, 2014
  5. RedVonix

    RedVonix

    Joined:
    Dec 13, 2011
    Posts:
    414
    Thank you for the helpful response (unlike the one above yours - of course I know I'm doing it wrong why do you think I'm asking for help? ;)). I'm still learning my way through Reflection so your help is very appreciated.

    I see what you are saying, makes sense! Okay. Though I'm not entirely sure how to create an object when the type is acquired from the parameter. I imagine it would be something like (and this is pseudo code):
    Code (csharp):
    1.  
    2. ObjectType(get object type from obj) newObj = parameter convert to object type
    3.  
    That is my guess at least... could you please supply me with an example however that would show how to create an object of a particular type when the type is acquired at runtime and not known in advance?
     
  6. Glader

    Glader

    Joined:
    Aug 19, 2013
    Posts:
    449
    If you have a Type instance of an object but you want to create an instance of that object you should use this:

    http://msdn.microsoft.com/en-us/library/vstudio/system.activator.createinstance

    Instantiating it via reflection. However, all of this is a VERY expensive operation. Be wary of reflection as it can hinder performance.

    Edit: As a warning Reflection changes a bit from 3.5 .Net to 4.5 .Net. So watch out as Unity is 3.5
     
    Last edited: Apr 29, 2014
  7. RedVonix

    RedVonix

    Joined:
    Dec 13, 2011
    Posts:
    414
    Glader,

    Thank you for the continued assistance and pointers, it's very appreciated!

    So to reduce the use of reflection, would it perhaps be best to have a series of functions for different parameter types? Something like this (pseudo code style again)

    Code (csharp):
    1.  
    2. void doStringFunction(dialogtype, stringtosend)
    3. {
    4.    Dialog = getDialogWithReflection
    5.    Method = getMethod
    6.    Method.invoke(object, stringtosend);
    7. }
    8.  
    9. void doIntFunction(dialogtype, inttosend)
    10. {
    11.    Dialog = getDialogWithReflection
    12.    Method = getMethod
    13.    Method.invoke(object, inttosend);
    14. }
    15.  
    16. Etc. For each parameter type used
    17.  
    That then would reduce one use of reflection. Would that be a more appropriate direction?
     
  8. shaderop

    shaderop

    Joined:
    Nov 24, 2010
    Posts:
    942
    I think what @KheltonHeadley actually meant wasn't that there's something wrong with how you're using reflection, but that attempting to solve your problem by using reflection in that particular way is not optimal.

    The following image kind of summarizes what comes to my mind when I look at your current implementation:

    $pipemaze.jpg

    I think you're pushing openness and flexibility to the point of breaking, and you're actually fighting against the .NET framework's type system instead of leveraging it to your advantage.

    I'm not sure what your system is supposed to do exactly, but here's a few ideas for you to consider:

    Instead of calling a method by using reflection and the method's name, why not create an interface named IDialogue that implements the methods that make sense for your usecase? e.g:
    Code (csharp):
    1. interface IDialogue
    2. {
    3.   void Show(Dictionary<string, object> parameters);
    4.   bool IsShowing();
    5.   IDialogue GetNextDialogue();
    6. }
    And instead of trying to figure out what parameters a method takes at runtime, stuff them all in the parameters dictionary shown in the example above and let each class that implement IDialogue extract the parameters it needs from that dictionary.

    Then you can use reflection to get all the types that implement IDialogue in your app (see this Stack Exchange question), but I think that even that can be avoided entirely, although there's no way to know for sure without knowing the specifics of your requirements.

    I could be wrong of course and it could be that your requirements can only be met with the way you're trying to get to work, but I think that's unlikely.

    Anyways, just my two cents. Make of it what you will.

    Good luck.
     
  9. RedVonix

    RedVonix

    Joined:
    Dec 13, 2011
    Posts:
    414
    Shaderop,

    That's actually a pretty cool idea, I hadn't thought of that! In my case the use of reflection still looks (I think? read below...) like the best way to achieve the result that's needed for this project, however your suggestion above regarding the use of an Interface actually answers some lingering questions in my mind. For one, I had seen people use Reflection to create extraordinarily generic enemies and items, which seemed great - however when I learned it's also not optimal, I kept wondering what could have been done to make those enemies and objects better, as generic enemies / objects do seem like a great way to implement very flexible setups.

    Actually in my case, the primary reason I used Reflection, was because I had integrated a main DialogScript object class, which is the master class for all parent Dialogs. So in my hierarchy, it looks a little like this:

    OkayDialog
    LoginDialog
    SettingsDialog

    Etc., and each of those is a child of DialogScript. I had integrated singletons for all of those also, and needed a quick way to access each Singleton. Rather than putting a singleton on each Child Class, I had wanted it in the DialogScript class, which resulted in a class definition like the following:

    Code (csharp):
    1.  
    2. public class DialogScript<T> : MonoBehaviour
    3.  
    At that point, it seemed like using reflection to access the methods was absolutely required, otherwise there was no way to access the objects due to the generic. So as a result, to pass a method into one of the singletons, I had to create a function that would get the singleton, determine its class, get the method, then invoke into it. The process then worked like this:

    Code (csharp):
    1.  
    2.  
    3. // This sends the request to call a method on an object
    4.   object[] vars = new object[]{InputTagManager.inputTags.None};
    5.   invokeDialogMethod(thisPopup, "TransitionDialogIn", vars);
    6.  
    7. // Then the invokeDialogMethod does the work...
    8.     private static void invokeDialogMethod(MainBoomUI.uiDialogs thisDialog, string methodString, object[] inObj)
    9.     {
    10.         if(thisDialog == MainBoomUI.uiDialogs.None)
    11.             return;
    12.  
    13.         try
    14.         {
    15.             var newDialogObj = new object();
    16.  
    17.                         // getDialogScriptForDialog is just a switch/case statement that sends back the proper Singleton for the thisDialog object pushed in
    18.             getDialogScriptForDialog<System.Type>(thisDialog, out newDialogObj);
    19.                    
    20.             Type type = newDialogObj.GetType();
    21.             var method = type.GetMethod(methodString);
    22.  
    23.             method.Invoke(newDialogObj, inObj);
    24.         }
    25.         catch
    26.         {
    27.             Debug.LogError("invokeDialogMethod failed when performing the string " + methodString + " on dialog " + thisDialog + " with the variable " + inObj[0] + ". This is completely fatal. Best call the digital coroner, IE, Dave.");
    28.         }
    29.     }
    30.  
    Again, I add that I'm somewhat new to using Reflection, so I'm open to any suggestions I can get, even if I'm using Reflection entirely wrong. (It's working well for this project, but I'd love to learn better uses of it for future projects).
     
    Last edited: Apr 29, 2014
  10. shaderop

    shaderop

    Joined:
    Nov 24, 2010
    Posts:
    942
    I'm still a bit sketchy on the structure of your code, but here it goes:

    If the only reason you're doing this is to stuff all the dialogue classes in a single GameObject that acts like a singleton, why not do it like this: Have a GameObject named, say, "DialogeContainer," and have a script on it that enforces the singleton behavior, i.e. ensures that only one instance loaded and all that jazz.

    And then on that same object have several MonoBehaviours, one for each of your dialogue classes. Use the singleton script to get a reference to that gameobject, and use GetComponent method to grab whichever DialogueScript you need.

    Reflection is a valid solution to some problems, but such problems should be few and far in between. C and C++ never had anything even remotely resembling run-time reflection, and yet programmers continue to write code in those languages just fine.