- Home /
Dictionary, Lambda expression & Action<,>
Hey,
I am trying to pass Dictionary entries as method calls. Currently, a member on these boards suggested I used System.Action, which is great for methods that don't take any parameters. In my case, my methods have parameters.
I am creating a Dictionary in order to check what is going to happen upon collision of two objects, instead of using an extensive switch. Thus, I have an IEnumerator which checks the distance between the two objects. Once it exits, it will invoke the appropriate method, by making a match from the string I am passing. This string is then matched with the Dictionary's entries and invokes the corresponding method.
private Dictionary<string, System.Action> moveUnitActions = new Dictionary <string, System.Action> ();
public void RemoveUnit (GameObject whichUnit) {
Destroy (whichUnit);
}
public IEnumerator MoveUnitTowardsUnit (GameObject whichUnit, GameObject whereTo, float speed, string collisionEvent) {
while (Vector3.Distance (whichUnit.transform.position, whereTo.transform.position) > 15f) {
whichUnit.transform.position = Vector3.MoveTowards (whichUnit.transform.position, whereTo.transform.position, Time.deltaTime * speed);
yield return null;
}
//block that checks for the proper action
}
moveUnitActions ["destroy"] = () => RemoveUnit(); //PROBLEM here.
I am aware that the line indicated as "PROBLEM here." is wrong. I tried so many different syntax combinations with lambda expressions that I no longer remember the original. In order to point the "destroy" entry to the RemoveUnit() method, I need a GameObject argument (because this is how the said method works). However, the gameobject will vary, depending on the unit I wish to destroy.
According to what I have seen, there are also Func and Action, with which I am not experienced. Is Action indeed the answer to my problem here and how do I modify the lambda expression? Finally, how will I invoke the method, once I find a match in the Dictionary? As simple as RemoveUnit(myUnit);?
Cheers :)
Answer by DoTA_KAMIKADzE · Apr 09, 2015 at 12:39 PM
I guess you want something like that?:
public void RemoveUnit(GameObject whichUnit)
{
Destroy(whichUnit);
}
private Dictionary<string, System.Action<GameObject>> moveUnitActions = new Dictionary<string, System.Action<GameObject>>();
private void populate()
{
moveUnitActions["destroy"] = RemoveUnit;
}
public void helloWorld()
{
GameObject obja = new GameObject();
//make sure that you have populated your dictionary before calling that:
moveUnitActions["destroy"](obja);
}
P.S. Here is an example for your updated question:
public void RemoveUnit(object whichUnit)
{
Destroy((GameObject)whichUnit);
}
private int someInt = 0;
public void AddIntegerAndPostString(object pass)
{
Tuple<List<int>, string> iInt = (Tuple<List<int>, string>)pass;
foreach (int o in iInt.Item1)
{
someInt += o;
}
Debug.Log(iInt.Item2);
}
private Dictionary<string, System.Action<object>> moveUnitActions = new Dictionary<string, System.Action<object>>();
private void populate()
{
moveUnitActions["destroy"] = RemoveUnit;
moveUnitActions["addPrint"] = AddIntegerAndPostString;
}
public void helloWorld()
{
GameObject obja = new GameObject();
//make sure that you have populated your dictionary before calling that:
moveUnitActions["destroy"](obja);
List<int> ilist = new List<int>();
ilist.Add(1);
ilist.Add(5);
Tuple<List<int>, string> iInt = new Tuple<List<int>, string>(ilist, "helloWorld");
moveUnitActions["addPrint"](iInt);
}
If you want to see a parameters as they are then you should create a custom Class/Interface (depending on the case) instead of adding functions to Dictionary.
If you need a returnable value from function then use:
Func<object, object>
instead of:
Action<object>
Hi, thanks for the reply. The problem is, I have multiple functions like RemoveUnit(). Each of them has a different set of parameters and their number ranges from 1 up to N. What I was hoping to get was that, when the match is found, simply invoke the method by passing the parameters right away.
Updated the answer - check the P.S. section of my answer.
I have seen your edited version and thank you for that. I have no idea whatsoever what the point of Tuple is (not judging, I literally have no idea) and the "pass" keyword on the right of the "=" operator. It looks like you have made a generic object parameter so that I can include other types of variables as well. What if I have a method like Fade (GameObject whichUnit, float howLong, string whichAnimationWhileFading, AudioClip whichClipToPlay)? This is not a real method, but I want to show you that except for the plain RemoveUnit() which takes only a game object, there are also other methods with variability in number of parameters and types. Assu$$anonymous$$g that the "types" is resolved by using "object" and then using typecasting, what about the number of parameters?
Pass is just the name of the parameter (look at the function parameters).
I used Tuple to pass multiple different values as a single object and then get them back.
You can use unlimited numbers of params in that way, though for example Tuple that I used is not implemented in Unity's library so that you'll need to implement it yourself in the way you like it with functionality you want it to have, alternatively you can use:
List<object>
or whatever else. For your method example above^ with my example in Answer you'll need to do something like that:
//Encapsulate it like that:
Tuple<GameObject,float,string,AudioClip> thisTuple = new Tuple<GameObject,float,string,AudioClip>(Add your variables here);
moveUnitActions["SomeFunction"](thisTuple);
//Then get the values back like that:
public void SomeFunction(object pass)
{
Tuple<GameObject, float, string, AudioClip> thisTuple = (Tuple<GameObject, float, string, AudioClip>)pass;
//thisTuple.Item1 -> gets the GameObject
//thisTuple.Item2 -> gets the float
//and so on
}
If you don't want to use list you can do it like that for example:
Action<object[]>
That will be a bit closer to Tuple I guess. An array of objects.
P.S. And if you'll do so then don't forget to correct my code by changing:
//this
object whichUnit
object pass
//on this:
object[] whichUnit
object[] pass
Though as for me a custom class with all functions in it and as you said "a switch" is a better idea if it will do what you expect.
Answer by blastproofgames · Apr 09, 2015 at 02:14 PM
Func and Action are delegates, thus function pointers. You can embed a function's property into a variable. Action returns void and Func returns the last parameter of the Func thus
Func<int, bool>
takes an int and returns a bool.
I think you need something as simple as this
Dictionary<string, Action> moveUnitActions = new Dictionary <string, Action> ();
void RemoveUnit(GameObject whichUnit)
{
moveUnitActions["objectName"] = () => Destroy(whichUnit);
}
If you see my IEnumerator, you will realize there is a string parameter called collisionEvent. This deter$$anonymous$$es the action to be taken. Thus, if I use $$anonymous$$oveUnitTowardsUnit (....., "destroy"), it will search in the dictionary "moveUnitActions". When it finds the string "destroy", it will invoke the corresponding method. This is primarily why I am using the dictionary, different strings, such as "destroy", "fade", "kill", etc. will point to a different method. However, as I previously mentioned, each method has a different set of parameters, which is the confusing part for the lambda expression syntax. Your way, to my understanding, first calls the method RemoveUnit and then it implements the pointer (the Action). The goal is for the script to find which method has to be called, based on the string parameter I am passing (collisionEvent).
Your answer
Follow this Question
Related Questions
Lambda + micro mscorlib 0 Answers
GET Request dont save data 0 Answers
Compiler error CS0309 when using nested generics and System.Action 0 Answers
anonymous functions with JavaScript? 2 Answers
add button listener via for loop 2 Answers