Skip to content

Instantly share code, notes, and snippets.

@dasannikov
Last active April 21, 2019 09:49
Show Gist options
  • Save dasannikov/c68cb57adf09c125db2edf2fe82d7254 to your computer and use it in GitHub Desktop.
Save dasannikov/c68cb57adf09c125db2edf2fe82d7254 to your computer and use it in GitHub Desktop.
using System;
using System.Text;
//----------------------------------------------------------------------------
// Using:
//
// private UpdateCheckString<int, int> _updateCheck;
// ...
// if(_updateCheck.IsChanged("Time now: {0}:{1}", DateTime.UtcNow.Minute, DateTime.UtcNow.Second))
// TextObject.text = _updateCheck;
//
//----------------------------------------------------------------------------
public struct UpdateCheckString<T1> where T1 : IEquatable<T1> {
private StringBuilder _sb;
private T1 _prevValue;
public static implicit operator string(UpdateCheckString<T1> t) {
return t._sb != null ? t._sb.ToString() : "";
}
public bool IsChanged(string format, T1 value) {
// Lazy init or check update
if(_sb == null) {
_sb = new StringBuilder(20);
_prevValue = value;
} else if(_prevValue.Equals(value)) {
return false;
}
// Update Text
_prevValue = value;
_sb.Clear();
_sb.AppendFormat(format, value);
return true;
}
}
//----------------------------------------------------------------------------
public struct UpdateCheckString<T1, T2> where T1 : IEquatable<T1> where T2 : IEquatable<T2> {
private StringBuilder _sb;
private T1 _prevValue1;
private T2 _prevValue2;
public static implicit operator string(UpdateCheckString<T1, T2> t) {
return t._sb != null ? t._sb.ToString() : "";
}
public bool IsChanged(string format, T1 value1, T2 value2) {
// Lazy init or check update
if(_sb == null) {
_sb = new StringBuilder(20);
_prevValue1 = value1;
_prevValue2 = value2;
} else if(_prevValue1.Equals(value1) && _prevValue2.Equals(value2))
return false;
// Update Text
_prevValue1 = value1;
_prevValue2 = value2;
_sb.Clear();
_sb.AppendFormat(format, value1, value2);
return true;
}
}
//----------------------------------------------------------------------------
public struct UpdateCheckString<T1, T2, T3>
where T1 : IEquatable<T1>
where T2 : IEquatable<T2>
where T3 : IEquatable<T3> {
private StringBuilder _sb;
private T1 _prevValue1;
private T2 _prevValue2;
private T3 _prevValue3;
public static implicit operator string(UpdateCheckString<T1, T2, T3> t) {
return t._sb != null ? t._sb.ToString() : "";
}
public bool IsChanged(string format, T1 value1, T2 value2, T3 value3) {
// Lazy init or check update
if(_sb == null) {
_sb = new StringBuilder(20);
_prevValue1 = value1;
_prevValue2 = value2;
_prevValue3 = value3;
} else if(_prevValue1.Equals(value1) && _prevValue2.Equals(value2) && _prevValue3.Equals(value3))
return false;
// Update Text
_prevValue1 = value1;
_prevValue2 = value2;
_prevValue3 = value3;
_sb.Clear();
_sb.AppendFormat(format, value1, value2, value3);
return true;
}
}
//----------------------------------------------------------------------------
public struct UpdateCheckString<T1, T2, T3, T4>
where T1 : IEquatable<T1>
where T2 : IEquatable<T2>
where T3 : IEquatable<T3>
where T4 : IEquatable<T4> {
private StringBuilder _sb;
private T1 _prevValue1;
private T2 _prevValue2;
private T3 _prevValue3;
private T4 _prevValue4;
public static implicit operator string(UpdateCheckString<T1, T2, T3, T4> t) {
return t._sb != null ? t._sb.ToString() : "";
}
public bool IsChanged(string format, T1 value1, T2 value2, T3 value3, T4 value4) {
// Lazy init or check update
if(_sb == null) {
_sb = new StringBuilder(20);
_prevValue1 = value1;
_prevValue2 = value2;
_prevValue3 = value3;
_prevValue4 = value4;
} else if(_prevValue1.Equals(value1) && _prevValue2.Equals(value2) && _prevValue3.Equals(value3) && _prevValue4.Equals(value4))
return false;
// Update Text
_prevValue1 = value1;
_prevValue2 = value2;
_prevValue3 = value3;
_prevValue4 = value4;
_sb.Clear();
_sb.AppendFormat(format, value1, value2, value3, value4);
return true;
}
}
//----------------------------------------------------------------------------
public struct UpdateCheckString<T1, T2, T3, T4, T5>
where T1 : IEquatable<T1>
where T2 : IEquatable<T2>
where T3 : IEquatable<T3>
where T4 : IEquatable<T4>
where T5 : IEquatable<T5> {
private StringBuilder _sb;
private T1 _prevValue1;
private T2 _prevValue2;
private T3 _prevValue3;
private T4 _prevValue4;
private T5 _prevValue5;
public static implicit operator string(UpdateCheckString<T1, T2, T3, T4, T5> t) {
return t._sb != null ? t._sb.ToString() : "";
}
public bool IsChanged(string format, T1 value1, T2 value2, T3 value3, T4 value4, T5 value5) {
// Lazy init or check update
if(_sb == null) {
_sb = new StringBuilder(20);
_prevValue1 = value1;
_prevValue2 = value2;
_prevValue3 = value3;
_prevValue4 = value4;
_prevValue5 = value5;
} else if(_prevValue1.Equals(value1) && _prevValue2.Equals(value2) && _prevValue3.Equals(value3) && _prevValue4.Equals(value4) && _prevValue5.Equals(value5))
return false;
// Update Text
_prevValue1 = value1;
_prevValue2 = value2;
_prevValue3 = value3;
_prevValue4 = value4;
_prevValue5 = value5;
_sb.Clear();
_sb.AppendFormat(format, value1, value2, value3, value4, value5);
return true;
}
}
@dasannikov
Copy link
Author

dasannikov commented Mar 23, 2018

Ключевая идея: Автоматически проверять изменения в переменных и если они есть обновлять строку

Требования:

  • Минимизирует аллокации памяти и работу GC
  • Боксинг только при непосредственном формировании строки (лучше вообще без него)
  • Работает быстро (главная цель - можно использовать в Unity Update)
  • Легко использовать (мало писать кода, в идеале применение в одну строку)

Чем плоха первая версия: (сейчас уже не первая :)

  • Значения не универсального типа (приходится в шаблоне указывать тип отслеживаемых параметров)
  • Тип параметров один на весь кэш (это неудобно когда один параметр например int а второй float)
  • Имя переменных нужно указывать при использовании 2 раза - в условии и при формировании строки (легко забыть и напутать)

В идеале я хотел бы достичь вот такого кода одной строкой (применения) в функции Update

_updateCheck(TextObject.text, "Time now:", DateTime.UtcNow.Minute, " : ", DateTime.UtcNow.Second);

Но что то мне подсказывает что это невозможно (учитывая требования). Сколько я не пробовал - возникает ненужный боксинг на этапе проверки на измение значения переменных.

@dasannikov
Copy link
Author

dasannikov commented Mar 23, 2018

Гипотетический пример использования

public class Player {
	public int PosX;
	public int PosY;
}

public class PlayerView {
	
	void Update1() {
		ScreenText = "Position: " + PlayerObject.PosX + ":" + PlayerObject.PosY;
	}

       void Update2() {
           if(UpdateCheck(PlayerObject.PosX, PlayerObject.PosY))
               ScreenText = "Position: " + PlayerObject.PosX + ":" + PlayerObject.PosY;
       }


	void UpdateIdeal() {
		UpdateCheck(ScreenText, "Position: ", PlayerObject.PosX, ":", PlayerObject.PosY);
	}
}

1й вариант плохой - мы создаем строку каждый раз
2й вариант - уже лучше но неудобен (см. коммент выше)
3й вариант идеальный?

Есть идеи как решить эту задачу лучше? (может быть в принципе нужен другой подход?)

@dasannikov
Copy link
Author

dasannikov commented Mar 24, 2018

Обновил. Результат вполне приемлем:

  • кэшируемые параметры могут быть произвольного типа
  • аллокаций практически нет
  • код теперь в структурах (не нужно создавать через new)
  • работает все очень быстро
  • нужно тиражировать код под разное кол-во параметров

Есть пожелания и замечания по улучшению?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment