Search Unity

Accessing a static variable of system.type

Discussion in 'Scripting' started by QuinnWinters, Mar 14, 2018.

  1. QuinnWinters

    QuinnWinters

    Joined:
    Dec 31, 2013
    Posts:
    494
    Is it possible to access a static variable in a system.type? In the following example I get the error System.type does not contain a definition for "instance."
    Code (CSharp):
    1. Type selectedClass;
    2.    
    3. selectedClass = typeof (Class1);
    4.  
    5. string temp = selectedClass.instance.textComponent.text;
    6.  
    7. //Where in Class1 there is the following:
    8. public static Class1 instance;
    9. public Text textComponent;
    10.  
    11. void Awake () {
    12.     instance = this;
    13. }
    I thought perhaps it just didn't like static variables so I tried removing the static variable and just using GetComponent on a referenced gameobject with the Class1 script on it but in that case I get the error "selectedClass does not exist in the current context."
     
  2. hasanbayat

    hasanbayat

    Joined:
    Oct 18, 2016
    Posts:
    630
    It is reflection, just do it like this to get the static variable "instance":
    Code (CSharp):
    1. Class1 myInstance = (Class1)selectedClass.GetProperty("instance", BindingFlags.Static | BindingFlags.Public).GetValue(null, null);
    Hope this helps.
    Thanks.
     
  3. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    You can't use a type variable as a type identifier:

    Code (csharp):
    1. //simplified version of what you're doing
    2. Type stringType = typeof(string);
    3. stringType myString = "lol"; //won't compile
    This is neither allowed syntactically or semantically.
    For the syntax, a declaration of a variable must be on the form 'typename variablename' or 'var variablename'. What you're writing is 'variablename variablename', where the first variable name is the variable "selectedClass".

    For the semantics, C# doesn't allow you to declare a variable of a type that's resolved at runtime like that. Whenever you declare a variable, it either has to have a type that can be resolved at compile time, or be generic.

    @hasanbayat's solution works. That being said, if you're doing reflection for runtime stuff (instead of metaprogramming for eg. serialization or testing), you're usually doing something wrong, and should probably be using generics instead. There are of course exceptions.
     
    Ellernate likes this.
  4. QuinnWinters

    QuinnWinters

    Joined:
    Dec 31, 2013
    Posts:
    494
    Thanks for the replies guys. I've been experimenting with generics on and off for the past week and it seems to be going right over my head.

    https://unity3d.com/learn/tutorials/topics/scripting/generics - doesn't provide much in the way of understandable functional examples, just isolated bits of code with no reference to the bigger picture.

    https://msdn.microsoft.com/en-us/library/ms379564 - is a giant wall of text that reads like stereo instructions and doesn't focus on practical usage within unity and I can't make heads or tails of most of what it's saying due to that.

    I've always learned from example rather than reading descriptions of things so since I can't find one I've been attempting to put together a simple functional example that accomplishes what I'm trying to do. Following the examples in the above two links and also from numerous other pages I've put together the following non-functional example, but it's majorly flawed.

    I'd be very grateful if anyone could help me correct this and get it functional, or perhaps point me to a similar example elsewhere.

    Script 1:
    Code (CSharp):
    1. //I don't need this script to be generic but I
    2. //can't use type T without adding <T> to it?
    3. public class ControllerClass <T> : MonoBehaviour {
    4.  
    5.     int choice = 0;
    6.     T chosenClass;
    7.  
    8.     void Start () {
    9.  
    10.         ChooseClass ();
    11.  
    12.         //Run the function within the chosen class singleton
    13.         chosenClass.ClassTest ();
    14.  
    15.     }
    16.  
    17.     public void ChooseClass () {
    18.  
    19.         //Choose between Class1 and Class2
    20.         choice = Random.Range (0, 1);
    21.  
    22.         if (choice == 0) chosenClass = Class1 <T>;
    23.         else chosenClass = Class2 <T>;
    24.  
    25.     }
    26.  
    27. }
    Scripts 2 and 3 with their names as Class1 and Class2 though I'm only posting the Class1 version:
    Code (CSharp):
    1. public class Class1 <T> : MonoBehaviour {
    2.  
    3.     public static Class1 <T> instance;
    4.  
    5.     void Awake () {
    6.  
    7.         instance = this;
    8.  
    9.     }
    10.  
    11.     public void ClassTest () {
    12.  
    13.         print ("Class 1 was accessed.");
    14.  
    15.     }
    16.  
    17. }
    18.  
    19. //A second class within the same script that can be dragged onto an object
    20. //so the generic class can access things dragged into it in the inspector
    21. public class Class1Variables : MonoBehaviour {
    22.  
    23.     public bool class1Bool = false;
    24.     public static Class1Variables instance;
    25.  
    26.     void Awake () {
    27.  
    28.         instance = this;
    29.  
    30.     }
    31.  
    32. }
     
  5. methos5k

    methos5k

    Joined:
    Aug 3, 2015
    Posts:
    8,712
    Just for clarity, are these class options going to be sharing a common base class or interface or do you want them to work with a 'Type'?

    Your example looks more like the former, but perhaps there's more to it?
     
  6. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    I think the best examples of generics and how they're usefull is the built-in generic collection types. So List<T>, Queue<T>, Stack<T>, Dictionary<TKey, TValue>, etc.

    There's also methods that are generic in non-generic classes. Good examples in Unity are GetComponent<T> and AddComponent<T>

    List is very straight-forward. It's a list of stuff. You can add things to it and remove things from it. The generic type here is the type of the things in the list. So List<int> is a list that contains ints, List<string> is a list that contains strings.

    You know all this!

    How this works behind the scenes is that the generic argument is used to create an array of the type, and make sure that everything put in it has the correct type. As an excercise to wrap your head around how generics work, I recommend implementing your own version of some of the simple collection types - like List and Stack.
     
  7. QuinnWinters

    QuinnWinters

    Joined:
    Dec 31, 2013
    Posts:
    494
    I'm wanting them to work with a Type, I think. The following is basically what I'm trying to accomplish: I have an if statement that refers to a class, but it could be one class or another depending on what an int in another script is. So:
    Code (CSharp):
    1. if (someColor.color == Class1.instance.colorValue) //Do something
    Where "Class1" could be any number of different singleton classes that each have a colorValue variable.

    As Baste suggested I've begun treating the classes similar to how I would treat a list. One thing I'm not understanding though is how a class could be an int, string or anything other than a class as I'm seeing them do in the unity tutorial on generics. A list as <int> I understand because it's a list of ints. But a class of <int>? Does that mean it would be a class of ints? Very confused by this bit.

    The current iteration of my controller class is as follows with something random as the class type since I don't know what to put the type as and Type doesn't seem to be a viable option without reflection.
    Code (CSharp):
    1. public class ControllerClass : MonoBehaviour {
    2.  
    3.     int choice = 0;
    4.     Class1 <int> class1 = new Class1 <int> ();
    5.     Class2 <int> class2 = new Class2 <int> ();
    6.     SomeUnknownType chosenClass;
    7.  
    8.     void Start () {
    9.  
    10.         ChooseClass ();
    11.  
    12.         //Run the function within the singleton instance of the chosen class
    13.         //Obviously doesn't work because chosenClass is of a type I just made up
    14.         chosenClass.ClassTest ();
    15.  
    16.     }
    17.  
    18.     public void ChooseClass () {
    19.  
    20.         //Choose between Class1 and Class2
    21.         choice = Random.Range (0, 1);
    22.  
    23.         if (choice == 0) chosenClass = class1.instance;
    24.         else chosenClass = class2.instance;
    25.  
    26.     }
    27.  
    28. }
     
  8. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    But, why? Are you doing something with the instance? Or is the color/instance just a marker used for some other behaviour?
    I think we're struggling to help you because we can't quite grasp what you're trying to do. Is this just experimentation?

    You could solve the if-statement above with all of the singletons inheriting from an interface that defined a method that returns a Color, and then adding all the singletons to a global list, but now we're moving into territory that's seeming more absurd than usefull.

    I see what you mean with the Unity tutorial, the text examples there are pretty dumb.

    The type parameter of a class means that any instance of the class has that specific type swapped in. So if you have a generic class Foo<T>, then a new foo = Foo<int> is a Foo that's restricted to int. So all fields that are declared as T are declared as int now, all methods that are declared with int returns or parameters now have int return parameters.

    So it's not a "class of int". It's a class containing ints, or doing things with ints or returning ints.

    To write it out, if this is the generic class:

    Code (csharp):
    1. public class Foo<T> {
    2.     public T someValue;
    3.     public T SomeMethod(string s, int i) { ... }
    4.     public bool SomeOtherMethod(T t1, T t2) { ... }
    5. }
    And you created an int-varant:
    Code (csharp):
    1. Foo<int> myFoo = new Foo<int>();
    It would be as if myFoo was an instance of a non-generic class that looked like this:
    Code (csharp):
    1. public class Foo {
    2.     public int someValue;
    3.     public int SomeMethod(string s, int i) { ... }
    4.     public bool SomeOtherMethod(int t1, int t2) { ... }
    5. }
     
  9. QuinnWinters

    QuinnWinters

    Joined:
    Dec 31, 2013
    Posts:
    494
    Aah now it makes sense. Thanks for the clarification.

    Yes, it's just one of those times where I looked at something and said "I wonder if that's possible. Lets find out!" You're right that in the example I posted a list could easily be made and accessed to accomplish the same goal but I was just curious if the list could be skipped and a direct call to the class instance as a variable could be used instead.