Search Unity

Help with reflection/generics

Discussion in 'Scripting' started by Dezeris85, Nov 19, 2019.

  1. Dezeris85

    Dezeris85

    Joined:
    Dec 16, 2016
    Posts:
    24
    I need help with a bit a code I'm stuck on. I've read a bunch and still not even sure how to go about what I want or if can be done.

    I want to be able to get a class type from a string variable and cast it into a generic method. I know it may be done with reflection.


    Code (CSharp):
    1. String w = "Weapon";
    2.     Type t = Type.getType(w);
    3.     SomeMethod<t>(arrray);
    4.  
    5.     Public Void SomeMethod<T>(T[] array) where T : class
    6.     {
    7.        //Do something
    8.     }
    9.  
    10.     Public class Weapon
    11.     {
    12.     }
    This doesnt work but gives an idea of what in trying to accomplish. Or I have an variable of that class if I could somehow get it from that even would work.
     
  2. StarManta

    StarManta

    Joined:
    Oct 23, 2006
    Posts:
    8,775
    I question how useful something like this would be, and maybe the example code abstracted it so far away that it appears useless, but - wouldn't you already know whatever you need to know about the "w" equivalent without needing to do getType on it? In other words, let's say this code is running in a function where "w" is getting passed in; can't you just use the type with which you receive "w" as the generic type? After all, if you don't know it ahead of time, it's not like SomeMethod would be able to do anything additional with a more specific type:
    Code (csharp):
    1. DoAThing("Weapon");
    2.  
    3. public void DoAThing(object w) {
    4.     SomeMethod<object>(array);
    5. }
    If this isn't applicable, then we'll need a LOT more context as far as what you're trying to actually accomplish.
     
  3. Dezeris85

    Dezeris85

    Joined:
    Dec 16, 2016
    Posts:
    24
    Basically it's a save feature for my backend when I want to add more items or whatever to my game using a editor I made... which takes all scriptable objects I made and turn them into a json string. which I'll load into the backend, and then players will be able to load all the items in the game with the json string.. I can accomplish this with more code, but if I could figure this out would be less coding. Then can just cycle threw a string[] of classes and pump out the json string I need.
     
  4. Boz0r

    Boz0r

    Joined:
    Feb 27, 2014
    Posts:
    419
    Generics are substituted during compile time, so you can't do that. You'll have to use reflection.
     
  5. Dezeris85

    Dezeris85

    Joined:
    Dec 16, 2016
    Posts:
    24
    I know that much.. just cant figure out how exactly to do it.
     
  6. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,329
    Invoking a generic method is pretty easy with reflection:

    Code (CSharp):
    1. private void InvokeSomeMethod(string genericTypeAsString)
    2. {
    3.     var genericType = GetType(genericTypeAsString);
    4.     var methodInfo = GetType().GetMethod("SomeMethod").MakeGenericMethod(genericType);
    5.     methodInfo.Invoke(this, null);
    6. }
    If you have the assembly qualified name of the type (get using Type.AssemblyQualifiedName), then getting the type is really easy too:

    Code (CSharp):
    1. public Type GetType(string assemblyQualifiedName)
    2. {
    3.     return Type.GetType(assemblyQualifiedName);      
    4. }
    If you only have its full name - or even the name without the namespace like in your example - then you'll need to just go through all types in the assembly until you find one that has the same name.

    Full name:

    Code (CSharp):
    1. public Type GetType(string fullName)
    2. {
    3.     var typeInSameAssembly = GetType();
    4.     var assembly = typeInSameAssembly.Assembly;
    5.     foreach(var type in assembly.GetTypes())
    6.     {
    7.         if(string.Equals(fullName, type.FullName, StringComparison.Ordinal))
    8.         {
    9.             return type;
    10.         }
    11.     }
    12.  
    13.     return null;
    14. }
    Name without namespace:

    Code (CSharp):
    1.  
    2. public Type GetType(string name)
    3. {
    4.     var typeInSameAssembly = GetType();
    5.     var assembly = typeInSameAssembly.Assembly;
    6.     foreach(var type in assembly.GetTypes())
    7.     {
    8.         if(string.Equals(name, type.Name, StringComparison.Ordinal))
    9.         {
    10.             return type;
    11.         }
    12.     }
    13.     return null;
    14. }
    15.  
     
    eisenpony and Dezeris85 like this.
  7. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Serialization is a component of programming that has been so well solved, you're almost always better off learning how to use the standard opensource offerings rather than do it yourself.

    For json, I recommend newtonsoft. I don't know for a fact that it ill work with Unity but I'll be surprised if it doesn't and even more surprised if there isn't some adaptation or alternative that does.
    https://github.com/JamesNK/Newtonsoft.Json

    Also, is there a reason the built in Unity serialization isn't a viable option for you?
     
  8. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,329
    I believe he is already using json serialization for most of the data.

    Delegate serialization is not very reliably supported in most of the commonly used serialization frameworks, as far as I know. That is probably the reason for the use of reflection when calling methods.
     
  9. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Are you saying he is serializing a delegate? I don't see that.
     
  10. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,329
    Well, the question was about getting from the name of a method to a way to call that method.

    In terms of serialization in general, he definitely already mentioned he's using json serialization:

     
  11. Dezeris85

    Dezeris85

    Joined:
    Dec 16, 2016
    Posts:
    24
    Yeah I'm already serializing the data, I was just wondering if there was a way to do so I wouldnt have to write out the code foreach class so. Especially if I add a class later .but instead just adding a new string for that class in a array of class names. I havent tried SisusCo code yet stuck at work, will try when I get home.
     
  12. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
    Agreed .. but I thought you brought up delegates? I could be wrong, but I don't think @Dezeris85 is trying to serialize delegates.

    Yes, you're totally right, and your post perfectly answers that. My comment was more about the "business" problem from the OP's second post. In this case, I think @Dezeris85 is just trying to do regular de/serialization of objects in a generic fashion, which is a problem already solved by dozens of people, including Unity.

    In my experience, when people ask about reflection, they are almost always making things more complicated than they need to be. I try to steer people away from reflection because a) most problems are solvable without it and b) it complicates the code significantly, not only for future programmers but also for the runtime.

    Many serialization frameworks, including newtonsoft.json, do exactly this. Yes, they usually use reflection internally, but they are also well tested, optimized and supported by other people.
     
  13. eisenpony

    eisenpony

    Joined:
    May 8, 2015
    Posts:
    974
  14. Dezeris85

    Dezeris85

    Joined:
    Dec 16, 2016
    Posts:
    24
    So far this works perfectly.
     
    Last edited: Nov 19, 2019
  15. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,329
    That was just my attempt to figure out how serialization could be used to solve the original problem. I was thinking that perhaps you meant that he could just hook up delegates in code and then let a serializer handle everything else.

    I think I maybe now understand where you're coming from.

    I personally tend to just assume that people have valid reasons for the things they are asking even if they haven't stated them outright, so unless I have evidence or strong suspicion to the contrary I don't usually question them. I'm also very much accustomed to using reflection, as I'm working on a custom inspector, where the whole concept revolves around using reflection to read data and then displaying it to the user :D

    Glad to hear it! :)

    JsonUtility can be really useful. It serializes data according to the same rules as unity normally does with things like your scene objects, so it is very intuitive to use. Json.NET is still more powerful and has its use cases, but in most scenarios JsonUtility is just perfect.
     
    eisenpony likes this.
  16. Dezeris85

    Dezeris85

    Joined:
    Dec 16, 2016
    Posts:
    24
    Alright I might of spoken too soon ... well kinda this works perfectly if its inside the same class but havent been able to get it to work once i moved it to its own class and tried calling another class;


    Calling Method from Class A thats in ClassB to involkeSomeMethod in ClassC



    Figuered it out for myself after taking a break from it for a while.... Had to write a GetMethod to locate the proper methodInfo to involke from... Here is the code for that..
    Code (CSharp):
    1. public MethodInfo GetMethod(string className,string methodName)
    2.     {
    3.         var classType = GetType(methodName);
    4.         var methods = classType.GetMethods();
    5.         foreach (var meth in methods)
    6.         {
    7.             if (string.Equals(className, meth.Name))
    8.             {
    9.                 return meth;
    10.             }
    11.         }
    12.         return null;
    13.     }
    you'll need the GetType method previously mentioned
     
    Last edited: Nov 20, 2019
  17. SisusCo

    SisusCo

    Joined:
    Jan 29, 2019
    Posts:
    1,329
    Yeah, my example just assumed that SomeMethod would exist in the same class with InvokeSomeMethod. You need to replace GetType() with the class type containing the method for it to work with other classes, as you already figured out for yourself.

    You've mixed up methodName and className in the code you posted btw. GetType(className) and string.Equals(methodName, meth.Name) would make more sense.