Unlimited method arguments without GC

  • A+
Category:Languages

I am trying to make a function that can receive unlimited amount of arguments without crating GC.

I know that this can be done with the params keyword but it creates GC. Also understand that you can pass array to to the function but I want to know if it is possible to pass unlimited method arguments without creating GC and without creating array or list and passing it to the List.

This is the example with the param code:

void Update() {     GameObject player1 = GameObject.Find("Player1");     GameObject player2 = GameObject.Find("Player2");      GameObject enemy1 = GameObject.Find("Enemy1");     GameObject enemy2 = GameObject.Find("Enemy2");     GameObject enemy3 = GameObject.Find("Enemy3");      Vector3 newPos = new Vector3(0, 0, 0);     moveObjects(newPos, 3f, player1, player2, enemy1, enemy2, enemy3); }  void moveObjects(Vector3 newPos, float duration, params GameObject[] objs) {     for (int i = 0; i < objs.Length; i++)     {         //StartCoroutine(moveToNewPos(objs[i].transform, newPos, duration));     } } 

When executed even with the StartCoroutine function commented out, it allocates 80 bytes. At first, I thought this was happening because I used the foreach loop then I changed that to a for loop but it still creating GC then I realized that params GameObject[] is causing it. See the Profiler below fore more visual information on this:

Unlimited method arguments without GC

So, how can I create a method that takes unlimited arguments without generating GC?

Please ignore the use of GameObject.Find function used in the Update function. That's just used for an example to get the reference of Objects I want during run-time. I have a script implemented to handle that but not related what's in this question.


Yes, it is possible to create a function with unlimited arguments without causing memory allocation.

You can do this with the undocumented __arglist keyword and wrapping our unlimited params inside it.

Change your moveObjects(newPos, 3f, player1, player2, enemy1, enemy2, enemy3) to moveObjects(newPos, 3f, __arglist(player1, player2, enemy1, enemy2, enemy3)).

In the moveObjects function, replace params GameObject[] objs with __arglist. Put the __arglist in ArgIterator then loop over it until ArgIterator.GetRemainingCount is no longer more than 0.

To obtain each value from the arguments in the loop, use ArgIterator.GetNextArg to get the TypedReference and then TypedReference.ToObject to cast the object to the Object type passed in the parameter which is GameObject in your example.

The whole changes together:

void Update() {     GameObject player1 = GameObject.Find("Player1");     GameObject player2 = GameObject.Find("Player2");      GameObject enemy1 = GameObject.Find("Enemy1");     GameObject enemy2 = GameObject.Find("Enemy2");     GameObject enemy3 = GameObject.Find("Enemy3");      Vector3 newPos = new Vector3(0, 0, 0);     moveObjects(newPos, 3f, __arglist(player1, player2, enemy1, enemy2, enemy3)); }  void moveObjects(Vector3 newPos, float duration, __arglist) {     //Put the arguments in ArgIterator     ArgIterator argIte = new ArgIterator(__arglist);      //Iterate through the arguments in ArgIterator     while (argIte.GetRemainingCount() > 0)     {         TypedReference typedReference = argIte.GetNextArg();         object tempObj = TypedReference.ToObject(typedReference);          GameObject obj = (GameObject)tempObj;         //StartCoroutine(moveToNewPos(obj.transform, newPos, duration));     } } 

While this should solve your problem, it's worth noting that it's an undocumented feature which means that it may stop working someday. If you care about that then an array should be used.

EDIT:

John Skeet mentioned about possible incompatibility on some platforms. I did a test again and it works on all the devices I tested it on. I did a test on both Windows and Android and it worked on both Windows and Android. I also expect it to work on iOS too. Too lazy to switch to Mac then fiddle with Xcode to test but there shouldn't be a problem.

Note you must use .NET>=4.6 to get this working

To do that:

1.Go to the Player Settings, change Scripting Runtime Version to "Experimental (.Net 4.6 Equivalent)"

2.Change Api Compatibility Level to .NET 4.6.

3.Change Scripting Backend to Mono instead of IL2CPP. IL2CPP is not supported because Unity did not implement this on it.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: