https://github.com/EveryGameSPlay/Package-Unity-Types.git
Данный пакет Я решил собрать для оптимизации рабочего процесса. Он включает в себя базовые типы, используемые мной при разработке различных проектов.
И хоть на данный момент пакет содержит не так много новых типов, однако каждый из них решает определенную задачу. Кроме того, в пакет включены методы расширения для некоторых базовых типов!
Основные типы:
- Option<T>
- Promise<T>
- Singleton<T>
Расширения:
- Float
- Int
- Linq
- Collections
- Unity Objects
Далее приведены примеры использования базовых типов и некоторых расширений.
Данный тип используется для предотвращения ошибок связанных с отсутствием значения. А также это удобный способ представить отсутствие значения. В отличие от Nullable, мы точно знаем что объект может быть получен в None форме (null для классов, default для структур). Nullable сообщает нам о возможности отсутствия значений только у структур, в то время как Option можно использовать для всех типов объектов.
- Проверка наличия обекта в его нормальном состоянии:
public bool DoThing(Option<Player> player)
{
// У типа Option проверяется свойство IsSome. В данном случае используется краткая форма записи.
// Полная форма - player.IsSome.
if (player)
{
// Некоторые действия.
return true;
}
return false;
}
- Пример использования со структурами:
public Option<int> GetNumber(...)
{
if(...)
return 100;
else
// Возвращаем отсутствующее значение.
return Option<int>.None;
}
Такой подход позволяет сразу уведомить нас о том, что объект может отсутствовать в его нормальном состоянии. Не нужно придумывать специальные коды по типу "-1" или других магических чисел.
- Пример использования с расширением LINQ:
public void ListSceneData(string name)
{
var scene = _scenes.FirstOrNone(x => x.name == name);
// Краткая форма записи scene.IsNone.
if(!scene)
return;
Debug.Log($"Идентификатор сцены - {scene.option.id}");
}
Свойство "option" - это экземпляр объекта. Scene в данном случае это и есть Option<Scene>.
- Пример использования в типе:
public class/struct Data
{
public Option<string> Name;
public Option<AnotherData> AnotherData;
}
Я использую Option везде, где хочу сохранить себе нервы и душевное спокойствие.
Данный тип полезен в ситуации, когда нам нужно получить некий объект, однако этот объект будет создан только в будущем. И для того чтобы не создавать события-фантомы (OnValueXCreated, OnValueXFailedCreation), можно использовать Promise для передачи данных. Кроме того, Promise сработает даже после первого оповещения: все новые подписчики получат экземпляр данных или уведомление об отсутствии данных.
- Пример иллюстрирующий разницу в использовании событий и типа Promise:
// Объект, использующий возможности типа Promise.
public class CoolObject
{
public Promise<Data> DataPromise;
}
// Стандартный объект без использования Promise
public class DefaultObject
{
private Data data;
public event Action<Data> OnDataCreated;
public event Action<object> OnDataFailed;
...
// И так для каждого отдельного члена объекта.
// Либо придется подвязывать все на один момент создания, когда все данные будут готовы.
...
}
- Пример взаимодействия с объектом типа Promise:
// Объект, использующий тип Promise.
public class CoolObject
{
public Promise<Data> DataPromise;
}
// Некоторый метод
private void LogDataFrom(CoolObject coolObj)
{
// Ожидаем значение, либо оповещение об ошибке в работе объекта.
coolObj.DataPromise.GetValue(x => LogData(x));
coolObj.DataPromise.GetFail (y => LogFail(y));
}
Для удобства используется Promise<TValue,int>, но вы можете использовать Promise<TValue,TFail> для определения своих данных об ошибке.
Эти объекты-синглтоны я реализовал для более удобного взаимодействия с подобными типами объектов. Две формы Singleton и SingletonRaw нужны для определения синглтона типа MonoBehaviour и синглтона типа object (функционально они одинаковы).
Основной целью при реализации были безопасность и стабильность. Безопасность заключается в возможности указать поведение lazy instance и обработать момент создания экземпляра. Стабильность осуществляется путем настройки поведения и обработки ситуаций существования и удаления экземпляра.
- Пример объявления Singleton:
// Синглтон типа MonoBehaviour (unity)
[LazyInstance(false)]
public class GameManager : Singleton<GameManager>
{
protected override bool CanBeDestroyedOutside => false
}
// Синглтон типа object (.net).
public class RawGameManager : SingletonRaw<RawGameManager> {...}
LazyInstance(false) атрибут говорит о том, что данный объект не может быть создан извне, например при обращении к GameManager.Instance (в большинстве других реализаций, при обращении к свойству экземпляра синглтон сразу же будет создан).
- Пример получения экземпляра:
public void DoSome()
{
// Проверяем существование синглтона.
if(GameManager.Exist)
return;
}
public void DoAnother()
{
// Вместо проверок на существование синглтона,
// Мы сразу обращаемся к Promise за экземпляром синглтона.
GameManager.InstancePromise.GetValue(x => DoWithGameManager(x))
}
public void DoUnsafe()
{
// Напрямую получаем экземпляр синглтона.
// Это лучше делать с реально вечными объектами, которые 100% уже существуют в памяти игры.
var instance = GameManager.Instance;
}
- Пример создания и уничтожения экземпляра:
// Удаление экземпляра, если он существует,
// С параметром immediate = true, означающим мгновенное удаление со сцены.
public void DestroySingleton() => SomeSingleton.DestroyIfExist(immediate = true);
// Простое создание экземпляра.
public void CreateSingleton () => SomeSingleton.CreateInstance();
Используется MIT License.
Kirill Gasanov - [email protected]