Search Unity

How to make this generic method?

Discussion in 'Scripting' started by techmage, Sep 17, 2014.

  1. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    So I've been trying to figure out the syntax to make this into a generic method. I can't quite get it. This is just a pretty simple singleton instancing get function that I want to make generic to pass in any type and have it do this process on that. Could anyone help me on this?

    Code (CSharp):
    1.     static public BundleManager sharedManager
    2.     {
    3.         get
    4.         {
    5.             if (_sharedManager == null)
    6.             {
    7.                 _sharedManager = FindObjectOfType(typeof(BundleManager)) as BundleManager;
    8.                 if (_sharedManager == null)
    9.                 {
    10.                     GameObject gameObject = new GameObject(typeof(BundleManager).ToString());
    11.                     _sharedManager = gameObject.AddComponent(typeof(BundleManager));
    12.                 }
    13.             }
    14.             return _sharedManager;
    15.         }
    16.     }    

    I want to end up with some function declaration like this:
    public T GetManager<T>(T manager);
    that I could then do
    _sharedManager = GetManager<BundleManager>(_sharedManager);

    Can't get the syntax figurer out though...
     
  2. techmage

    techmage

    Joined:
    Oct 31, 2009
    Posts:
    2,133
    The closest I can get is this:

    Code (CSharp):
    1.     private static T GetManager<T>(T manager)
    2.     {
    3.         if (manager == null)
    4.         {
    5.             manager = (T)FindObjectOfType(typeof(T));
    6.             if (manager == null)
    7.             {
    8.                 GameObject gameObject = new GameObject(typeof(T).ToString());
    9.                 manager = (T)gameObject.AddComponent(typeof(T));
    10.             }
    11.         }
    12.         return manager;
    13.     }
    But it gives errors like this:
    Assets/VZUnityCommon/Scripts/Manager/BundleManager.cs(29,38): error CS0030: Cannot convert type `UnityEngine.Object' to `T'
    Assets/VZUnityCommon/Scripts/Manager/BundleManager.cs(33,46): error CS0030: Cannot convert type `UnityEngine.Component' to `T'
     
  3. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    That's a pretty weird pattern. You're returning something but what you're returning is actually a mutated version of what you're passing into it. I would probably just return it and also constrain the method so you can't pass bogus types in

    Code (csharp):
    1.  
    2. static T GetManager<T>() where T : UnityEngine.Component { }
    3.  
    4. _sharedManager = GetManager<BundleManager>();
    5.  
    Also - generic arguments are meant to provide compile-time safety so things get weird when you start trying to evaluate what T is inside a method. Your best bet might be to cache singleton instances by the string representations of their types but I'd defer to someone who's better at generics than I am.
     
    Last edited: Sep 17, 2014
  4. GarthSmith

    GarthSmith

    Joined:
    Apr 26, 2012
    Posts:
    1,240
    This looks a lot like the Service Locator Pattern.
    http://www.c-sharpcorner.com/UploadFile/dacca2/service-locator-design-pattern/

    So the biggest problem is that in your non-generic version, you only have one manager. Here you can pass in a ton of types so you really need a dictionary of managers.

    We use a Dictionary keyed by System.Type and the dictionary values are the services. (At Tic Toc Games, we actually wrap the service in another class so we can literally use any class as a service.)

    *NOT TESTED*
    Code (csharp):
    1. private static T GetManager<T>() where T : Component
    2. {
    3.   T managerToReturn = null;
    4.   // Check to see if manager already exists.
    5.   if (allManagers.TryGetValue(typeof(T), out managerToReturn))
    6.     return managerToReturn;
    7.   // Manager does not already exist. Act normal.
    8.   managerToReturn = (T)FindObjectOfType(typeof(T));
    9.   if (managerToReturn == null)
    10.   {
    11.     GameObject newManager = new GameObject(typeof(T).ToString());
    12.     managerToReturn = newManager.AddComponent<T>();
    13.   }
    14.   // Put the new manager in the Dictionary of managers.
    15.   allManagers.Add(typeof(T), managerToReturn);
    16.   return managerToReturn;
    17. }
    18.  
    19. private static Dictionary<System.Type, Component> allManagers;
     
    Last edited: Sep 18, 2014