Is it possible to add a generic delegate Action to a List collection? I need some kind of simple messaging system for a Silverlight application.
UPDATE The following is what i realy "want"
class SomeClass<T>
{
public T Data { get; set; }
// and more ....
}
class App
{
List<Action<SomeClass<T>>> _actions = new List<Action<SomeClass<T>>>();
void Add<T>( Action<SomeClass<T>> foo )
{
_actions.Add( foo );
}
}
Compiler:
The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)
initial code snipped class SomeClassBase { }
class SomeClass<T> : SomeClassBase
{
public T Data { get; set; }
// and more ....
}
class App
{
List<Action<SomeClassBase>> _actions = new List<Action<SomeClassBase>>();
void Add<T>( Action<SomeClass<T>> foo )
where T : SomeClassBase
{
_actions.Add( foo );
}
}
The compiler complains - for the _actions.Add() line;
Argument 1: cannot convert from 'System.Action<test.SomeClass<T>>' to 'System.Action<test.SomeClassBase>'
The best overloaded method match for 'System.Collections.Generic.List<System.Action<test.SomeClassBase>>.Add(System.Action<test.SomeClassBase>)' has some invalid arguments
From the application side there is no need for the SomeClassBase class, yet it seems impossible to define a List of Action<SomeClass<T>>
elements and the approach with the base-class works when using the class in the List, instead of the Action
Thanks, jochen
EDIT: Okay, now I see what you're trying to do. I've left the old answer below for posterity :)
Unfortunately you can't express the relationship you want in C# generics, but as you can make sure you're the only one manipulating the collection, you can keep it safe yourself:
Try this:
class App
{
private readonly Dictionary<Type, object> delegateMap;
void Add<T>(Action<SomeClass<T>> foo)
{
object tmp;
if (!delegateMap.TryGetValue(typeof(T), out tmp))
{
tmp = new List<Action<SomeClass<T>>>();
delegateMap[typeof(t)] = tmp;
}
List<Action<SomeClass<T>> list = (List<Action<SomeClass<T>>) tmp;
list.Add(foo);
}
void InvokeActions<T>(SomeClass<T> item)
{
object tmp;
if (delegateMap.TryGetValue(typeof(T), out tmp))
{
List<Action<SomeClass<T>> list = (List<Action<SomeClass<T>>) tmp;
foreach (var action in list)
{
action(item);
}
}
}
}
Note that you could use the fact that delegates are multicast to just keep a Dictionary<Type, Delegate>
and combine them together, but I'll leave that as an exercise for the reader :)
Old answer
It's failing for a good reason. Let's get rid of the generics (as they're irrelevant here) and think about a simpler case - fruit and bananas.
You're trying to add an Action<Banana>
to a List<Action<Fruit>>
. You can't do that - even with the generic variance of C# 4. Why? Because it's not safe. Consider this:
Action<Banana> peeler = banana => banana.Peel();
List<Action<Fruit>> fruitActions = new List<Action<Fruit>>();
fruitActions.Add(peeler); // Nope!
fruitActions[0].Invoke(new Strawberry());
Eek! Now we've got a banana peeler trying to peel a strawberry... what a mess!
Not that the other way round would be acceptable in C# 4:
Action<Fruit> eater = fruit => fruit.Eat();
List<Action<Banana>> bananaActions = new List<Action<Banana>>();
fruitActions.Add(eater); // Yes!
fruitActions[0].Invoke(new Banana());
Here we're adding an Action<Fruit>
to a List<Action<Banana>>
- that's acceptable, because anything you can do to an Action<Banana>
is also valid for an Action<Fruit>
.