Reputation: 7128
In my application I have the need to reference various types of Action
Action action1;
Action<int> action2;
Action actRef = action2; // Error: actRef is not the same type as action2
Basically I would like a type with which I can reference both action1
and action2
. Is this possible?
The best solution I can think of is to wrap the Action<...>
in another class that implements some interface.
public interface MyInterfaceType
{
}
public class MyActionWrapper : MyInterfaceType
{
public Action MyAction { get; set; }
}
public class MyActionWrapper<T> : MyInterfaceType
{
public Action<T> MyAction { get; set; }
}
Then carry around references to 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);
}
}
}
Is there a better solution? Or a way to clean this solution up?
More background on the specifics. I have written a simple scripting language which I am trying to parse into Action
, each Action
represents a parsed command from the script. As one commentor pointed out I can easily just wrap an Action
with another
Action myParameterlessAction = new Action(() => MySingleParmAction(1));
though for a few of my commands I allow the user to supply a psuedo argument/variable. For example I might have an argument FOO
that allows one argument. The argument is either a value for FOO
or it is a "variable" meaning the current value of FOO
when the command is executed.
FOO 231 // Set FOO = 231
FOO VAL_FOO // Set FOO to the desired value of FOO, whatever it may be at this time
So when I go to parse this second type of command I create an Action
that takes a parameter so I can supply it when the time comes.
Action fooCommand1 = new Action(delegate() { MyForm.SetFooValue(231); });
Action<int> fooCommand2 = MyForm.SetFooValue;
I would like to be able to collect all Action
create from parsing into a List<...>
that I can then loop over and execute each action.. (I do realize that I will need a method to determine which Action
need parameters supplied and how to determine what to supply them with; based on my solution above this information would be in MyInterfaceType
most likely).
EDIT: In response to @The Anathema's answer, I have a command in my language that allows a user to wait for some period of time before continuing. The parsing of the script to the Action
would look like this
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);
Will Action.DynamicInvoke
block until the Task.Delay(...)
has completed? Where will the invocation happen, on the current thread or the thread on which the Action
was created? Can I change either of these options?
Also per this SO answer I take it it would be worthwhile to try to resolve the Action
s that are parameterless to Action myAct
so that I can Invoke
them instead of DynamicInvoke
(Seeing that DynamicInvoke
takes orders of magnitude longer to execute per the linked answer)?
Upvotes: 0
Views: 576
Reputation: 759
You can reference them as the Delegate
type.
Delegate action1 = new Action(DoStuff);
Delegate action2 = new Action<int>(DoStuff2);
You can even iterate over an IEnumerable<Delegate>
and dynamically invoke each of them:
var myActions = new List<Delegate>();
myActions.Add(action1);
myActions.Add(action2);
foreach (var action in myActions)
{
action.DynamicInvoke();
}
However, keep in mind that you will get an TargetParameterCountException
if you're calling one without the appropriate args passed into the DynamicInvoke() method.
You can go a step further and have a dictionary that stores the arguments.
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);
}
If you need to call the same actions multiple times, change Dictionary<Delegate, object[]>
to List<KeyValuePair<Delegate, object[]>>
. This will allow you to have duplicate keys (actions).
Upvotes: 2