How to correctly call a method of generic type T?

To put you in context, in my Unity project I have a Master script and some generic functions to unify some functionalities.
My problem is, the generic function SpawnPrefab() has to know which prefab must spawn, which is provided by the GetPrefab() function defined in the generic type. So I made this approach to make it work:

First I created an interface like this:

interface IObject
{
   GameObject GetPrefab();
}

class MyObject : IObject
{
   public MyObject() { ... }

   // The function that needs to be called inside the generic one
   public GameObject GetPrefab() { ... }
}

And then, in Master’s class:

static void SpawnRandom<T> where T : IObject, new()
{
   T Instance = new T();
   GameObject prefab = Instance.GetPrefab();
   
   // Instantiates the prefab
   ...
}

Notice that I have to create a new generic object before calling the method. I thought in making the function GetPrefab static, but interfaces doesn’t support them. So is it the correct way?

If not, my question is: what’s the correct way to call the function GetPrefab()? Is it necessary to instantiate a T type in order to call the method?

  • C# interfaces don’t support static members, as you discovered. So yes, you need an instance to call a method from an interface.
  • Yes you can do it this way but it’s strange to say the least. Usually you would use the “new()” constraint for a “Factory” pattern, as per these examples: new constraint - C# reference | Microsoft Learn

What’s the end goal here?

2 Likes

First of all, thank you for your reply.

The problem presented in the thread it’s very simplified. The real one it’s a bit more complex.
What I really want to do is get the nearest object of type T to the player. To accomplish that, I created this function:

GameObject GetNearest<T>() where T : IObject, new()
{
    GameObject closestObj = null;
    List<GameObject> list;
    float minDistance = Mathf.Infinity;

   T instance = new T();
   list = instance.InstantiatedObjects();      
  
   // Iterates over the list to find the nearest object
   ...       

   return closestObj;
}

This function uses a list of already cached objects, so I don’t have to abuse FindObjectsOfType(), because it’ll drop the performance. These lists are stored in another script, and there’s one for each object type.

At first, to get the appropiate list, I used as placeholder something like this:

if (typeof(T) == typeof(Food))
   list = Master.FoodList;
else if (typeof(T) == typeof(Table))
   list = Master.TableList

But the number of types were increasing, so I needed the function to be even more generic.
So, to get the list, I made somewhat what I posted in the original problem.

interface IObject
{
   List<GameObject> GetInstantiatedObjects();
}


class MyObject : IObject
{
   public MyObject() { ... }

   // The function that needs to be called inside the generic one
   public List<GameObject> GetInstantiatedObjects() { ... }
}

And there’s another problem: As far as I know, I shouldn’t be instantiating classes that inherits from MonoBehaviour, because the editor gives this error:
“You are trying to create a MonoBehaviour using the ‘new’ keyword. This is not allowed.”

So, creating a new T and calling the method in this way isn’t the best solution if the generic type inhertis from MonoBehaviour.

I would probably just use a single Dictionary<Type, List<GameObject>> to solve this problem, either on a singleton object or static. Something like this (partial pseudocode):

static Dictionary<Type, List<GameObject>> cache = new Dictionary<Type, List<GameObject>>();

public static void AddInstance(Component instance) {
  var t = instance.GetType();
  List<GameObject> list;
  if (!cache.TryGetValue(t, out list)) {
    // Didn't find list in dictionary - create new one
    list = new List<GameObject>();
    cache[t] = list;
  }

  list.Add(instance.GameObject);
}

public static List<GameObject> GetInstances<T>() {
  if (cache.TryGetValue(typeof(T), out List<GameObject> list) {
    return list;
  }

  // return empty list?
}
1 Like

I totally forgot about Dictionaries. Thank you so much!