将通用Action <T>委托添加到列表中

joc*_*hen 12 c# generics collections delegates action

是否可以将通用委托Action添加到List集合中?我需要某种用于Silverlight应用程序的简单消息传递系统.

更新 以下是我真正"想要"的内容

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 );
    }
}
Run Code Online (Sandbox Code Playgroud)

编译:

The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?)
Run Code Online (Sandbox Code Playgroud)

初始代码剪切 类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 );
    }
}
Run Code Online (Sandbox Code Playgroud)

编译器抱怨 - 对于_actions.Add()行;

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
Run Code Online (Sandbox Code Playgroud)

从应用程序端,不需要SomeClassBase类,但是在List中Action<SomeClass<T>>使用类而不是Action时,似乎无法定义元素列表和基类的方法.

谢谢,jochen

Jon*_*eet 17

编辑:好的,现在我看到你正在尝试做什么.为了后人,我已经把下面的旧答案留给了:)

遗憾的是,您无法在C#generics中表达您想要的关系,但是因为您可以确保您是唯一一个操纵该集合的人,您可以自己保护它:

试试这个:

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);
             }
         }
     }
}
Run Code Online (Sandbox Code Playgroud)

请注意,您可以使用委托是多播的事实来保持Dictionary<Type, Delegate>并将它们组合在一起,但我会将其留作读者的练习:)


老答案

这是有充分理由失败的.让我们摆脱仿制药(因为它们在这里无关紧要)并考虑一个更简单的案例 - 水果和香蕉.

你正在尝试添加Action<Banana>一个List<Action<Fruit>>.你不能这样做 - 即使是C#4的泛型差异.为什么?因为它不安全.考虑一下:

Action<Banana> peeler = banana => banana.Peel();
List<Action<Fruit>> fruitActions = new List<Action<Fruit>>();
fruitActions.Add(peeler); // Nope!
fruitActions[0].Invoke(new Strawberry());
Run Code Online (Sandbox Code Playgroud)

伊克!现在我们有一个香蕉削皮器试图剥一个草莓......真糟糕!

并不是说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());
Run Code Online (Sandbox Code Playgroud)

在这里我们添加Action<Fruit>一个List<Action<Banana>>- 这是可以接受的,因为你可以对a做的任何事情Action<Banana>也适用于Action<Fruit>.


Bri*_*sio 6

这会做你想要的吗?

void Add<T>(Action<SomeClass<T>> foo)
    where T : SomeClassBase
{
    _actions.Add(x => foo((SomeClass<T>) x));
}
Run Code Online (Sandbox Code Playgroud)


Mar*_*sen 1

不确定这是否是您想要的。但尝试将您的 Add 方法更改为:

void Add( Action<SomeClassBase> foo )
{
   _actions.Add( foo );
}
Run Code Online (Sandbox Code Playgroud)

更新

这将允许你做这样的事情:

App app = new App();

Action<SomeClass<int>> action = null; // Initilize it...

app.Add((Action<SomeClassBase>)action);
Run Code Online (Sandbox Code Playgroud)