引用各种Action< ...>单一类型的变量类型?

时间:2016-03-08 17:40:14

标签: c# delegates

在我的应用程序中,我需要引用各种类型的Action

Action action1;
Action<int> action2; 

Action actRef = action2; // Error: actRef is not the same type as action2

基本上我想要一种可以引用action1action2的类型。这可能吗?

我能想到的最佳解决方案是将Action<...>包装在另一个实现某个接口的类中。

public interface MyInterfaceType
{

}

public class MyActionWrapper : MyInterfaceType
{
    public Action MyAction { get; set; }
}

public class MyActionWrapper<T> : MyInterfaceType 
{
    public Action<T> MyAction { get; set; }
}

然后随身携带对MyInterfaceType的引用。

public class MyActionsHolder
{
    List<MyInterfaceType> MyActions { get; set; }

    public void DoActions()
    {
        foreach(MyInterfaceType anAction in MyActions)
        {
            if(anAction is MyActionWrapper)
                ((MyActionWrapper)anAction).MyAction();
            else if(anAction is MyActionWrapper<int>)
                ((MyActionWrapper<int>)anAction).MyAction(1);
        }
    }
}

有更好的解决方案吗?还是一种清理这个解决方案的方法?

有关具体细节的更多背景知识。我编写了一个简单的脚本语言,我试图将其解析为Action,每个Action代表一个来自脚本的解析命令。正如一位评论员指出的那样,我可以轻松地将Action与另一个

包装起来
Action myParameterlessAction = new Action(() => MySingleParmAction(1));

虽然我的一些命令允许用户提供伪造的参数/变量。例如,我可能有一个允许一个参数的参数FOO。参数可以是FOO的值,也可以是&#34;变量&#34;表示执行命令时FOO的当前值。

FOO 231     // Set FOO = 231
FOO VAL_FOO // Set FOO to the desired value of FOO, whatever it may be at this time

因此,当我去解析第二种类型的命令时,我创建了一个Action,它接受​​一个参数,以便我可以在时机到来时提供它。

Action fooCommand1 = new Action(delegate() { MyForm.SetFooValue(231); });
Action<int> fooCommand2 = MyForm.SetFooValue;

我希望能够收集所有Action创建解析成List<...>然后我可以循环并执行每个动作..(我确实知道我需要一个方法来确定提供哪些Action需要的参数以及如何确定提供给他们的内容;根据我上面的解决方案,此信息最有可能在MyInterfaceType中。

编辑:为了回应@The Anathema的回答,我用我的语言命令允许用户等待一段时间再继续。将脚本解析为Action看起来像这样

public KeyValuePair<Delegate, object[]> CommandAction { get; set; }
...
Action commandDelegate = new Action(async delegate()
{
    await Task.Delay(msWait);
});
result.CommandAction = new KeyValuePair<Delegate, object[]>(commandDelegate, null);

Action.DynamicInvoke完成之前,Task.Delay(...)会阻止吗?在当前线程或创建Action的线程上,调用将在何处发生?我可以更改其中任何一个选项吗?

同样根据这个SO answer,我认为尝试解决Action无参数的Action myAct是值得的,这样我就可以Invoke而不是DynamicInvoke DynamicInvoke(看到// ==UserScript== // @name TL momentjs // @namespace http://tampermonkey.net/ // @version 0.1 // @description add moment js to tl! // @author You // @match http://www.teamliquid.net/forum/ // @require https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.11.2/moment.js // @grant none // ==/UserScript== /* jshint -W097 */ 'use strict'; // Your code here... $('.forumindex:nth-child(3n+4)').each(function(function() { datestr = $(this)[0].innerText.split(",")[0]; user = $(this)[0].innerText.split(",")[1]; console.log( $(this)[0].innerText = moment(datestr).fromNow() + ", " + user); }); 根据链接的答案执行的时间要长几个数量级?)

1 个答案:

答案 0 :(得分:2)

您可以将其引用为Delegate类型。

        Delegate action1 = new Action(DoStuff);
        Delegate action2 = new Action<int>(DoStuff2);

你甚至可以迭代IEnumerable<Delegate>并动态调用它们:

        var myActions = new List<Delegate>();
        myActions.Add(action1);
        myActions.Add(action2);

        foreach (var action in myActions)
        {
            action.DynamicInvoke();
        }

但是,请记住,如果您在没有传递给DynamicInvoke()方法的适当参数的情况下调用一个TargetParameterCountException,您将获得 Delegate action1 = new Action(DoStuff); Delegate action2 = new Action<int>(DoStuff2); var myActions = new Dictionary<Delegate, object[]>(); myActions.Add(action1, null); myActions.Add(action2, new object[] { 0 }); foreach (var action in myActions) { action.Key.DynamicInvoke(action.Value); }

您可以更进一步,拥有一个存储参数的字典。

Dictionary<Delegate, object[]>

如果您需要多次调用相同的操作,请将List<KeyValuePair<Delegate, object[]>>更改为using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations.Schema; using System.Linq; using System.Web; namespace MVCTest.Models { [Table("Employee")] public class EmployeeModel { public int EmployeeId { get; set; } public string name {get;set;} } } 。这将允许您有重复的键(操作)。