Scott Scott - 12 days ago 6
C# Question

c# generic delegate to manage instantiated objects

I'm trying to figure out how to use a generic delegate to manage my instantiated objects in a game engine.

Here is some pseudo-code to demonstrate what I'm trying to do:

public class ObjectManager
{
public delegate void ObjectManagerEvent <T> (T instantiatedObject);
public ObjectManagerEvent <T> onObjectInstantiated;


public void InstantiateObject (Object objToInstantiate)
{
var obj = SomeInternalInstantiateMethod ();

ObjectManagerEvent _onObjectInstantiated = onObjectInstantiated;
if (_onObjectInstantiated != null)
{
_onObjectInstantiated (obj);
}
}
}


public class Shape : EBehaviour {}
public class Animal : EBehaviour {}


public class DemoShape
{
private void Init ()
{
ObjectManager.onObjectInstantiated += OnObjectInstaniated;
}

public void OnObjectInstaniated (Shape shape)
{
// do something with shape
}
}

public class DemoAnimal
{
private void Init ()
{
ObjectManager.onObjectInstantiated += OnObjectInstaniated;
}

public void OnObjectInstaniated (Animal animal)
{
// do something with animal
}
}


I know that
public ObjectManagerEvent <T> onObjectInstantiated ();
would throw an error, but I'm just kind of lost on how to achieve what I want.

Any pointers?

PMV PMV
Answer

First, your delegate syntax is very C# 1.0.

Option 1

You can't do this in a particularly simple and elegant way because in C# you cannot use an open generic type to declare a generic event. The closest that we can do is create a dictionary of objects, each of which has an event, and we can use generic methods to access this dictionary.

I also assume you intend InstantiateObject to create and return a new instance. Here I also assume everything is a class with a parameterless constructor.

public static class ObjectManager
{
    public class TypeEvent<T>
    {
        // Our event handler will accept a parameter of type T and return void
        public event Action<T> OnObjectInstantiated;

        public void RaiseObjectInstantiated(T obj)
        {
            OnObjectInstantiated?.Invoke(obj);
        }
    }

    private static Dictionary<Type, object> _typeMap = new Dictionary<Type, object>();

    public static TypeEvent<T> ForType<T>() where T: class, new()
    {

        Type t = typeof(T);
        if (!_typeMap.ContainsKey(t))
        {
            _typeMap[t] = new TypeEvent<T>();
        }
        return _typeMap[t] as TypeEvent<T>;

    }

    public static T InstantiateObject<T>() where T: class, new()
    {
        T obj = new T();
        ForType<T>().RaiseObjectInstantiated(obj);
        return obj;
    }
}

You could use it like so:

        ObjectManager.ForType<Foo>().OnObjectInstantiated += fooInstantiated;
        Foo f = ObjectManager.InstantiateObject<Foo>();

Option 2

If you are okay with making ObjectManager itself a static generic class, you could greatly simplify this. Note this means you no longer have just one ObjectManager class - ObjectManager<Foo> and ObjectManager<Bar> are now different classes with different variables. If that's acceptable to you, this makes things a lot cleaner for the small bit you've told us that ObjectManager needs to do:

public static class ObjectManager<T> where T : class, new()
{
    // Our event handler will accept a parameter of type T and return void
    public static event Action<T> OnObjectInstantiated;

    public static T InstantiateObject() 
    {
        T obj = new T();
        OnObjectInstantiated?.Invoke(obj);
        return obj;
    }
}