Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store Action delegates with constrained generic type in a type safe collection in C#?

Let's say I want to store delegates in a collection like this:

public void AddDelegate<T>(Action<T> action) where T : ISomething
{
   _delegates.Add(action);
}

What should the type of _delegates be?

Trying with IList<Action<ISomething>> _delegates; results in error message Argument type 'System.Action<T>' is not assignable to parameter type 'System.Action<ISomething>' for the Add call above. But why doesn't it work? The compiler should 'know' that T must be an ISomething. What are my options if I don't want to loose type safety by using e.g. a IList<object> or parameterize the whole class on the generic type T?

like image 881
Christian Avatar asked Dec 03 '25 23:12

Christian


2 Answers

_delegates has to be of type IList<Action<T>>.

Therefore you have to add the <T> where T : ISomething to the class.

Alternatively, do away with the generics and support Action<ISomething> directly.

So your two options are:

public class Delegates<T> where T : ISomething
{
    private List<Action<T>> _delegates;

    public void AddDelegate(Action<T> action)
    {
        _delegates.Add(action);
    }
}

Or

public class Delegates
{
    private List<Action<ISomething>> _delegates;

    public void AddDelegate(Action<ISomething> action)
    {
        _delegates.Add(action);
    }
}

Edit As Sehnsucht points out, there's a third option: wrap the Action<T> delegate inside an Action<ISomething> delegate and then the OP can achieve what he originally wanted.

The reason for this is that whilst T is a "subtype" (implementer) of ISomething, Action<T> is not a subtype of Action<ISomething>, in fact as dcastro explains in his answer, the opposite is actually true (despite that seeming counter-intuitive). Even with casting, you cannot add an instance of Action<T> to a list of Action<ISomething>.

like image 119
David Arno Avatar answered Dec 06 '25 15:12

David Arno


you should see here for more insight to the problem

I can propose that "kind of hack" to circumvent the problem but I can't say if it's a good thing or really just a hack.

    void Add<T> (Action<T> action) where T : ISomething
    {
        Action<ISomething> typedAction = something => action ((T) something);
        _delegates.Add (typedAction);
    }
like image 26
Sehnsucht Avatar answered Dec 06 '25 14:12

Sehnsucht