Utility 包使用说明
概述
Utility 包定义了工具类层。Utility 提供无状态的辅助功能,如数学计算、文件操作、序列化等通用工具方法。与 System 不同,Utility 不依赖架构状态,是纯粹的工具函数集合。
核心接口
IUtility
Utility 标记接口,所有工具类都应实现此接口。
接口定义:
csharp
public interface IUtility
{
// 标记接口,无方法定义
}IContextUtility
上下文工具接口,扩展了IUtility接口,为需要感知架构上下文的工具类提供基础能力。
接口定义:
csharp
public interface IContextUtility : IUtility
{
void Init(); // 初始化上下文工具
}核心类
AbstractContextUtility
抽象上下文工具类,提供上下文相关的通用功能实现。继承自 ContextAwareBase 并实现 IContextUtility 接口。
使用方式:
csharp
public abstract class AbstractContextUtility : ContextAwareBase, IContextUtility
{
protected ILogger Logger = null!;
void IContextUtility.Init()
{
var name = GetType().Name;
Logger = LoggerFactoryResolver.Provider.CreateLogger(name);
Logger.Debug($"Initializing Context Utility: {name}");
OnInit(); // 子类实现初始化逻辑
Logger.Info($"Context Utility initialized: {name}");
}
protected abstract void OnInit(); // 子类实现具体的初始化逻辑
}基本使用
1. 定义 Utility
csharp
// 存储工具类,继承自AbstractContextUtility
public class StorageUtility : AbstractContextUtility
{
private const string SavePath = "user://save_data.json";
protected override void OnInit()
{
Logger.Info("StorageUtility initialized");
}
public void Save<T>(T data)
{
string json = JsonSerializer.Serialize(data);
// 实际保存逻辑
File.WriteAllText(SavePath, json);
}
public T Load<T>()
{
if (!File.Exists(SavePath))
return default(T);
string json = File.ReadAllText(SavePath);
return JsonSerializer.Deserialize<T>(json);
}
public void Delete()
{
if (File.Exists(SavePath))
{
File.Delete(SavePath);
}
}
}
// 数学工具类,作为普通Utility
public class MathUtility : IUtility
{
public float Lerp(float a, float b, float t)
{
return a + (b - a) * Math.Clamp(t, 0f, 1f);
}
public bool IsInRange(float value, float min, float max)
{
return value >= min && value <= max;
}
}
// 时间工具类
public class TimeUtility : IUtility
{
public string FormatTime(float seconds)
{
int minutes = (int)(seconds / 60);
int secs = (int)(seconds % 60);
return $"{minutes:D2}:{secs:D2}";
}
public long GetCurrentTimestamp()
{
return DateTimeOffset.UtcNow.ToUnixTimeSeconds();
}
public bool IsExpired(long timestamp, int durationSeconds)
{
return GetCurrentTimestamp() > timestamp + durationSeconds;
}
}2. 注册 Utility
csharp
public class GameArchitecture : Architecture
{
protected override void Init()
{
// 注册 Utility(不需要初始化)
this.RegisterUtility<StorageUtility>(new StorageUtility());
this.RegisterUtility<MathUtility>(new MathUtility());
this.RegisterUtility<TimeUtility>(new TimeUtility());
}
}3. 使用 Utility
csharp
// 在 System 中使用
public class SaveSystem : AbstractSystem
{
protected override void OnInit()
{
this.RegisterEvent<SaveGameEvent>(OnSaveGame);
this.RegisterEvent<LoadGameEvent>(OnLoadGame);
}
private void OnSaveGame(SaveGameEvent e)
{
var storage = this.GetUtility<StorageUtility>();
var playerModel = this.GetModel<PlayerModel>();
var saveData = new SaveData
{
PlayerName = playerModel.Name.Value,
Level = playerModel.Level.Value,
Gold = playerModel.Gold.Value,
Timestamp = this.GetUtility<TimeUtility>().GetCurrentTimestamp()
};
storage.Save(saveData);
this.SendEvent(new GameSavedEvent());
}
private void OnLoadGame(LoadGameEvent e)
{
var storage = this.GetUtility<StorageUtility>();
var saveData = storage.Load<SaveData>();
if (saveData != null)
{
var playerModel = this.GetModel<PlayerModel>();
playerModel.Name.Value = saveData.PlayerName;
playerModel.Level.Value = saveData.Level;
playerModel.Gold.Value = saveData.Gold;
this.SendEvent(new GameLoadedEvent());
}
}
}
// 在 Command 中使用
public class MovePlayerCommand : AbstractCommand
{
public Vector3 TargetPosition { get; set; }
public float Speed { get; set; }
protected override void OnExecute()
{
var playerModel = this.GetModel<PlayerModel>();
var mathUtil = this.GetUtility<MathUtility>();
// 使用工具类计算
Vector3 currentPos = playerModel.Position.Value;
Vector3 direction = (TargetPosition - currentPos).Normalized();
Vector3 newPos = currentPos + direction * Speed;
playerModel.Position.Value = newPos;
}
}常见 Utility 类型
1. 序列化/反序列化工具
csharp
public class JsonUtility : IUtility
{
public string Serialize<T>(T obj)
{
return Json.Stringify(obj);
}
public T Deserialize<T>(string json) where T : new()
{
return Json.Parse<T>(json);
}
public bool TryDeserialize<T>(string json, out T result) where T : new()
{
try
{
result = Json.Parse<T>(json);
return true;
}
catch
{
result = default;
return false;
}
}
}2. 随机数工具
csharp
public class RandomUtility : IUtility
{
private Random _random = new Random();
public int Range(int min, int max)
{
return _random.Next(min, max + 1);
}
public float Range(float min, float max)
{
return min + (float)_random.NextDouble() * (max - min);
}
public T Choose<T>(params T[] items)
{
return items[Range(0, items.Length - 1)];
}
public List<T> Shuffle<T>(List<T> list)
{
var shuffled = new List<T>(list);
for (int i = shuffled.Count - 1; i > 0; i--)
{
int j = Range(0, i);
(shuffled[i], shuffled[j]) = (shuffled[j], shuffled[i]);
}
return shuffled;
}
public bool Probability(float chance)
{
return _random.NextDouble() < chance;
}
}3. 字符串工具
csharp
public class StringUtility : IUtility
{
public string Truncate(string text, int maxLength, string suffix = "...")
{
if (text.Length <= maxLength)
return text;
return text.Substring(0, maxLength - suffix.Length) + suffix;
}
public string FormatNumber(int number)
{
if (number >= 1000000)
return $"{number / 1000000.0:F1}M";
if (number >= 1000)
return $"{number / 1000.0:F1}K";
return number.ToString();
}
public string ToTitleCase(string text)
{
return System.Globalization.CultureInfo.CurrentCulture.TextInfo.ToTitleCase(text.ToLower());
}
public bool IsValidEmail(string email)
{
return Regex.IsMatch(email, @"^[^@\s]+@[^@\s]+\.[^@\s]+$");
}
}4. 加密工具
csharp
public class EncryptionUtility : IUtility
{
private const string EncryptionKey = "YourSecretKey123";
public string Encrypt(string plainText)
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(plainText);
byte[] encrypted = EncryptBytes(data);
return Convert.ToBase64String(encrypted);
}
public string Decrypt(string encryptedText)
{
byte[] data = Convert.FromBase64String(encryptedText);
byte[] decrypted = DecryptBytes(data);
return System.Text.Encoding.UTF8.GetString(decrypted);
}
private byte[] EncryptBytes(byte[] data)
{
// 简单的 XOR 加密示例(实际项目应使用更安全的算法)
byte[] key = System.Text.Encoding.UTF8.GetBytes(EncryptionKey);
byte[] result = new byte[data.Length];
for (int i = 0; i < data.Length; i++)
{
result[i] = (byte)(data[i] ^ key[i % key.Length]);
}
return result;
}
private byte[] DecryptBytes(byte[] data)
{
return EncryptBytes(data); // XOR 解密与加密相同
}
}5. 对象池工具
csharp
public class ObjectPoolUtility : IUtility
{
private Dictionary<Type, Queue<object>> _pools = new();
public T Get<T>() where T : new()
{
Type type = typeof(T);
if (_pools.ContainsKey(type) && _pools[type].Count > 0)
{
return (T)_pools[type].Dequeue();
}
return new T();
}
public void Return<T>(T obj)
{
Type type = typeof(T);
if (!_pools.ContainsKey(type))
{
_pools[type] = new Queue<object>();
}
_pools[type].Enqueue(obj);
}
public void Clear<T>()
{
Type type = typeof(T);
if (_pools.ContainsKey(type))
{
_pools[type].Clear();
}
}
public void ClearAll()
{
_pools.Clear();
}
}6. 日志工具
csharp
public class LogUtility : IUtility
{
public enum LogLevel
{
Debug,
Info,
Warning,
Error
}
public void Log(string message, LogLevel level = LogLevel.Info)
{
string prefix = level switch
{
LogLevel.Debug => "[DEBUG]",
LogLevel.Info => "[INFO]",
LogLevel.Warning => "[WARN]",
LogLevel.Error => "[ERROR]",
_ => ""
};
string timestamp = DateTime.UtcNow.ToString("HH:mm:ss");
GD.Print($"{timestamp} {prefix} {message}");
}
public void Debug(string message) => Log(message, LogLevel.Debug);
public void Info(string message) => Log(message, LogLevel.Info);
public void Warning(string message) => Log(message, LogLevel.Warning);
public void Error(string message) => Log(message, LogLevel.Error);
}Utility vs System
Utility(工具层)
- 无状态 - 不存储业务数据
- 纯函数 - 相同输入产生相同输出
- 独立性 - 不依赖架构状态
- 可复用 - 可在多个项目中使用
System(逻辑层)
- 有状态 - 可能存储临时状态
- 业务逻辑 - 处理特定业务流程
- 架构依赖 - 需要访问 Model
- 项目特定 - 针对特定项目设计
csharp
// ✅ Utility: 无状态的工具方法
public class MathUtility : IUtility
{
public float CalculateDamage(float attackPower, float defense)
{
return Math.Max(1, attackPower - defense * 0.5f);
}
}
// ✅ System: 有状态的业务逻辑
public class CombatSystem : AbstractSystem
{
private List<CombatInstance> _activeCombats = new();
protected override void OnInit()
{
this.RegisterEvent<AttackEvent>(OnAttack);
}
private void OnAttack(AttackEvent e)
{
var mathUtil = this.GetUtility<MathUtility>();
var playerModel = this.GetModel<PlayerModel>();
// 使用 Utility 计算,但在 System 中处理业务逻辑
float damage = mathUtil.CalculateDamage(e.AttackPower, playerModel.Defense.Value);
playerModel.Health.Value -= (int)damage;
_activeCombats.Add(new CombatInstance { Damage = damage });
}
}最佳实践
- 保持无状态 - Utility 不应存储业务状态
- 纯函数优先 - 相同输入应产生相同输出
- 单一职责 - 每个 Utility 专注于一类功能
- 避免依赖 - 不依赖 Model、System 等架构组件
- 可测试 - 易于单元测试的纯函数
错误示例
csharp
// ❌ 错误:Utility 中存储状态
public class BadUtility : IUtility
{
private int _counter = 0; // 不应该有状态
public int GetNextId()
{
return ++_counter; // 依赖内部状态
}
}
// ❌ 错误:Utility 中访问 Model
public class BadUtility2 : IUtility
{
public void DoSomething()
{
// Utility 不应该访问架构组件
var model = this.GetModel<PlayerModel>(); // 编译错误!
}
}
// ✅ 正确:如果需要状态,应该使用 System
public class IdGeneratorSystem : AbstractSystem
{
private int _counter = 0;
protected override void OnInit() { }
public int GetNextId()
{
return ++_counter;
}
}性能优化
1. 缓存计算结果
csharp
public class PathfindingUtility : IUtility
{
private Dictionary<(Vector3, Vector3), List<Vector3>> _pathCache = new();
public List<Vector3> FindPath(Vector3 start, Vector3 end, bool useCache = true)
{
var key = (start, end);
if (useCache && _pathCache.ContainsKey(key))
{
return _pathCache[key];
}
var path = CalculatePath(start, end);
if (useCache)
{
_pathCache[key] = path;
}
return path;
}
private List<Vector3> CalculatePath(Vector3 start, Vector3 end)
{
// A* 算法等复杂计算...
return new List<Vector3>();
}
public void ClearCache()
{
_pathCache.Clear();
}
}2. 对象复用
csharp
public class CollectionUtility : IUtility
{
private List<Vector3> _tempList = new();
public List<Vector3> GetPointsInRadius(Vector3 center, float radius, List<Vector3> points)
{
_tempList.Clear();
foreach (var point in points)
{
if (point.DistanceTo(center) <= radius)
{
_tempList.Add(point);
}
}
return new List<Vector3>(_tempList); // 返回副本
}
}相关包
system- System 中使用 Utilitycommand- Command 中可以使用 Utilityarchitecture- 在架构中注册 Utilityioc- Utility 通过 IoC 容器管理extensions- 提供 GetUtility 扩展方法