Неограниченные аргументы метода без GC
Я пытаюсь сделать функцию,которая может получать неограниченное количество аргументов без создания GC.
Я знаю, что это можно сделать с помощью params
ключевое слово, но оно создает GC. Также поймите, что вы можете передать массив функции, но я хочу знать, можно ли передать неограниченные аргументы метода без создания GC и без создания массива или списка и передачи его в список.
это пример с param
код:
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));
}
}
при выполнении даже с StartCoroutine
функция прокомментировала, она выделяет 80 байт. Сначала я думал, что это происходит, потому что я использовал foreach
цикл, то я изменил это на for
цикл, но он все еще создает GC, тогда я понял, что params GameObject[]
вызывает его. См. профилировщик ниже для получения дополнительной визуальной информации об этом:
Итак, как я могу создать метод, который принимает неограниченное аргументы без получения ГК?
Пожалуйста, игнорируйте использование . Это просто используется для примера, чтобы получить ссылку на объекты, которые я хочу во время выполнения. У меня есть скрипт, реализованный для обработки этого, но не связанный с тем, что в этом вопросе.
3 ответов
да, можно создать функцию с неограниченными аргументами, не вызывая выделения памяти.
вы можете сделать это с помощью недокументированных __arglist
ключевое слово и упаковка наш неограниченный params
внутри него.
изменить moveObjects(newPos, 3f, player1, player2, enemy1, enemy2, enemy3)
to moveObjects(newPos, 3f, __arglist(player1, player2, enemy1, enemy2, enemy3))
.
на
если вы используете params
и укажите аргументы таким образом, тогда да, это будет всегда создавайте объект массива.
если вы хотите избежать этого, и если вам не нужно беспокоиться о рекурсии или безопасности потоков, вы можете сохранить List<GameObject>
вокруг и использовать один и тот же список несколько раз. Например:
private readonly List<GameObject> objectListCache = new List<GameObject>();
private void Update()
{
cachedObjectList.Clear();
cachedObjectList.Add(GameObject.Find("Player1"));
cachedObjectList.Add(GameObject.Find("Player2"));
cachedObjectList.Add(GameObject.Find("Enemy1"));
cachedObjectList.Add(GameObject.Find("Enemy2"));
cachedObjectList.Add(GameObject.Find("Enemy3"));
Vector3 newPos = new Vector3(0, 0, 0);
moveObjects(newPos, 3f, cachedObjectList);
cachedObjectList.Clear();
}
void MoveObjects(Vector3 newPos, float duration, List<GameObject> objs)
{
foreach (GameObject obj in objs)
{
// ...
}
}
когда вы очистите List<T>
это установит все элементы внутреннего буфера в null, но не отбросит буфер-поэтому его можно использовать снова для следующий Update
вызов без какого-либо выделения.
ответ прямо перед вами (Vector3). Сделайте супер специализированный struct
:
public struct GameObjectParams3 // 3 only for example, you need more
{
private int NextGet;
public GameObject Object0;
public GameObject Object1;
public GameObject Object2;
public GameObjectParams3(GameObject g0) : this(g0, null, null) { }
public GameObjectParams3(GameObject g0, GameObject g1) : this(g0, g1, null) { }
public GameObjectParams3(GameObject g0, GameObject g1, GameObject g2)
{
this.NextGet = 0;
this.Object0 = g0;
this.Object1 = g1;
this.Object2 = g2;
}
public GameObject Next()
{
GameObject ret;
switch (this.NextGet)
{
case 0: ret = Object0; break;
case 1: ret = Object1; break;
case 2: ret = Object2; break;
default: return null;
}
this.NextGet++;
return ret;
}
/*
public int Length { get { return this.NextPush; } }
private int NextPush;
public void Push(GameObject go)
{
switch (this.NextPush)
{
case 0: Object0 = go; break;
case 1: Object1 = go; break;
case 2: Object2 = go; break;
default: throw new IndexOutOfRangeException();
}
this.NextPush++;
}
*/
}
и использовать, как вы используете Vector3;
void Update()
{
// ...
var objs = new GameObjectParams3(enemy1, enemy2, enemy3);
// Or:
//
// var objs = new GameObjectParams3();
// objs.Object0 = GameObject.Find("Player1");
// Better, make ObjectN private and:
//
// var objs = new GameObjectParams3();
// objs.Push( GameObject.Find("Player1") );
//
// To have .Length afterwards.
Vector3 newPos = new Vector3(0, 0, 0);
moveObjects(newPos, 3f, objs);
}
void moveObjects(Vector3 newPos, float duration, GameObjectParams3 objs)
{
while (true)
{
var obj = objs.Next();
if (obj == null)
return;
//StartCoroutine(moveToNewPos(objs[i].transform, newPos, duration));
}
/*
var len = objs.Length;
for (int n = 0; n < len; n++)
{
var obj = objs.Next();
StartCoroutine(moveToNewPos(objs[i].transform, newPos, duration));
}
*/
}