news 2026/7/6 13:04:33

Unity MyFramework:框架里的代码简化技巧(三)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Unity MyFramework:框架里的代码简化技巧(三)

前两篇写了 MyFramework 里一些常用的代码简化技巧。

这一篇继续写第三组。

不过这次只写真正能让调用侧代码变短的东西。

也就是一眼能看到:

原本要写几行,现在一行就能表达清楚。

这类工具不是复杂系统。

它们更多是项目长期开发中沉淀出来的小函数、小扩展、小短写。

单独看都不大,但使用频率很高。

项目地址:

https://github.com/ZHOURUIH/MyFramework


一、字符串和数字转换短写

项目里数字和字符串互转非常常见。

比如:

  • 配置表解析
  • UI 文本显示
  • 日志输出
  • 协议调试
  • 坐标输出
  • 百分比显示
  • bool 状态输出

原始写法通常是这样:

int id = int.Parse(idString); float speed = float.Parse(speedString); string valueText = value.ToString("F2"); string flag = enable ? "true" : "false";

这些代码本身不难。

但问题是项目里到处都要写。

MyFramework 最新版本里,这类转换已经大量改成扩展方法。

比如字符串转整数:

int id = idString.SToI();

字符串转浮点:

float speed = speedString.SToF();

整数转字符串:

string idText = id.IToS();

long 转字符串:

string guidText = guid.LToS();

浮点转字符串,并指定精度:

string valueText = value.FToS(2);

bool 转字符串:

string flag = enable.boolToString();

这类写法的价值不是 C# 原生做不到,而是项目里需要统一。

比如 bool 转字符串,如果到处写:

string flag = enable ? "true" : "false";

后面又有人写:

string flag = enable ? "True" : "False";

再有人写:

string flag = enable.ToString();

项目里的输出格式就不统一。

统一成:

string flag = enable.boolToString();

以后风格就固定了。

真实代码里,bool 转字符串是这样实现的:

public static string boolToString(this bool value, bool firstUpper = false, bool fullUpper = false) { if (fullUpper) { return value ? "TRUE" : "FALSE"; } if (firstUpper) { return value ? "True" : "False"; } return value ? "true" : "false"; }

百分比显示也是一样。

原本写法:

string percent = (value * 100.0f).ToString("F1") + "%";

现在写法:

string percent = value.toPercent();

或者指定精度:

string percent = value.toPercent(1);

真实代码:

public static string toPercent(this string value, int precision = 1) { return (value.SToF() * 100).FToS(precision) + "%"; } public static string toPercent(this float value, int precision = 1) { return (value * 100).FToS(precision) + "%"; }

向量转字符串也统一成扩展方法。

原本写法:

string posText = pos.x.ToString("F2") + "," + pos.y.ToString("F2") + "," + pos.z.ToString("F2");

现在写法:

string posText = pos.V3ToS(2);

真实代码:

public static string V3ToS(this Vector3 value, int precision = 4) { return strcat(value.x.FToS(precision), ",", value.y.FToS(precision), ",", value.z.FToS(precision)); }

反过来,字符串转向量也可以一行完成。

原本写法:

string[] splitList = str.Split(','); Vector3 pos = new(float.Parse(splitList[0]), float.Parse(splitList[1]), float.Parse(splitList[2]));

现在写法:

Vector3 pos = str.SToV3();

真实代码:

public static Vector3 SToV3(this string str, char separate = ',') { if (str.isEmpty() || str == "0,0,0") { return Vector3.zero; } string[] splitList = str.split(separate); if (splitList.count() < 3) { return Vector3.zero; } return new(splitList[0].SToF(), splitList[1].SToF(), splitList[2].SToF()); }

这类短函数的意义很明确:

把常见转换写成统一入口。

它不会让逻辑变复杂。

只是让代码更短、更统一。


二、List 操作短写

List<T>是项目里最常用的容器之一。

很多 List 操作原本都要写好几行。

比如条件添加。

原本写法:

if (condition) { list.Add(value); }

现在写法:

list.addIf(value, condition);

真实代码:

public static bool addIf<T>(this List<T> list, T value, bool condition) { if (condition) { list.Add(value); } return condition; }

非空才添加。

原本写法:

if (value != null) { list.Add(value); }

现在写法:

list.addNotNull(value);

真实代码:

public static bool addNotNull<T>(this List<T> list, T value) where T : class { if (value != null) { list.Add(value); return true; } return false; }

字符串非空才添加。

原本写法:

if (!text.isEmpty()) { list.Add(text); }

现在写法:

list.addNotEmpty(text);

真实代码:

public static bool addNotEmpty(this List<string> list, string value) { if (!value.isEmpty()) { list.Add(value); return true; } return false; }

不重复添加。

原本写法:

if (!list.Contains(value)) { list.Add(value); }

现在写法:

list.addUnique(value);

真实代码:

public static bool addUnique<T>(this List<T> list, T value) { if (!list.Contains(value)) { list.Add(value); return true; } return false; }

有条件地不重复添加。

原本写法:

if (condition && !list.Contains(value)) { list.Add(value); }

现在写法:

list.addUniqueIf(value, condition);

真实代码:

public static bool addUniqueIf<T>(this List<T> list, T value, bool condition) { if (!condition) { return false; } if (!list.Contains(value)) { list.Add(value); return true; } return false; }

根据状态添加或移除。

原本写法:

if (enable) { if (!list.Contains(value)) { list.Add(value); } } else { list.Remove(value); }

现在写法:

list.addUniqueOrRemove(value, enable);

真实代码:

public static void addUniqueOrRemove<T>(this List<T> list, T value, bool addOrRemove) { if (addOrRemove) { list.addUnique(value); } else { list.Remove(value); } }

移除并返回被移除的元素。

原本写法:

T value = list[index]; list.RemoveAt(index); return value;

现在写法:

return list.removeAt(index);

真实代码:

public static T removeAt<T>(this List<T> list, int index) { T value = list[index]; list.RemoveAt(index); return value; }

不关心顺序时,可以交换到末尾再删除。

现在写法:

return list.swapToEndAndRemove(index);

真实代码:

public static T swapToEndAndRemove<T>(this List<T> list, int index) { list.swap(index, list.Count - 1); return list.removeAt(list.Count - 1); }

创建新对象并添加到列表。

原本写法:

Item item = new(); list.Add(item); item.init();

现在写法:

list.addNew<Item>().init();

真实代码:

public static T addNew<T>(this List<T> list) where T : new() { return list.add(new()); }

如果添加的是对象池对象,也可以写:

list.addClass<Item>().init();

真实代码:

public static T addClass<T>(this List<T> list) where T : ClassObject, new() { return list.add(CLASS<T>()); }

取最后一个元素。

原本写法:

T value = list == null || list.Count == 0 ? default : list[list.Count - 1];

现在写法:

T value = list.getLast();

这类扩展函数都不复杂。

但它们解决的是同一种问题:

不要让业务代码里到处充满重复的 if、Contains、Add、RemoveAt。

比如:

list.addUnique(value);

这一行表达得很清楚:

把 value 添加进列表,但不要重复。

这比每个地方都手写Contains + Add更稳定,也更统一。


三、Dictionary 操作短写

Dictionary的重复代码也很多。

最常见的是安全取值。

原本写法:

if (!dic.TryGetValue(key, out Value value)) { value = defaultValue; }

现在写法:

Value value = dic.get(key, defaultValue);

真实代码:

public static Value get<Key, Value>(this Dictionary<Key, Value> map, Key key, Value defaultValue) { return map != null && map.TryGetValue(key, out Value value) ? value : defaultValue; }

不需要默认值时:

Value value = dic.get(key);

真实代码:

public static Value get<Key, Value>(this Dictionary<Key, Value> map, Key key) { if (map == null) { return default; } map.TryGetValue(key, out Value value); return value; }

添加或覆盖。

原本写法:

dic[key] = value;

现在写法:

dic.addOrSet(key, value);

真实代码:

public static void addOrSet<TKey, TValue>(this Dictionary<TKey, TValue> dic, TKey key, TValue value) { dic[key] = value; }

这个看起来只是换了名字,但它表达更明确:

这个操作允许新增,也允许覆盖。

数值累加是更明显的简化。

原本写法:

if (dic.TryGetValue(key, out int curValue)) { dic[key] = curValue + increase; } else { dic.Add(key, increase); }

现在写法:

dic.addOrIncreaseValue(key, increase);

真实代码:

public static void addOrIncreaseValue<TKey>(this Dictionary<TKey, int> dic, TKey key, int increase) { if (dic.TryGetValue(key, out int curValue)) { dic[key] = curValue + increase; } else { dic.Add(key, increase); } }

根据状态添加或移除。

原本写法:

if (isAdd) { dic.Add(key, value); } else { dic.Remove(key); }

现在写法:

dic.addOrRemove(key, value, isAdd);

真实代码:

public static void addOrRemove<Key, Value>(this Dictionary<Key, Value> map, Key key, Value value, bool isAdd) { if (isAdd) { map.Add(key, value); } else { map.Remove(key); } }

获取或创建普通对象。

原本写法:

if (!map.TryGetValue(key, out Value value)) { value = new(); map.Add(key, value); }

现在写法:

map.getOrAddNew(key, out Value value);

或者:

Value value = map.getOrAddNew(key);

真实代码:

public static bool getOrAddNew<Key, Value>(this Dictionary<Key, Value> map, Key key, out Value value) where Value : new() { if (!map.TryGetValue(key, out value)) { value = new(); map.Add(key, value); return false; } return true; } public static Value getOrAddNew<Key, Value>(this Dictionary<Key, Value> map, Key key) where Value : new() { if (!map.TryGetValue(key, out Value value)) { value = new(); map.Add(key, value); } return value; }

获取或创建对象池对象。

原本写法:

if (!map.TryGetValue(key, out T value)) { CLASS(out value); map.Add(key, value); }

现在写法:

T value = map.getOrAddClass(key);

真实代码:

public static T getOrAddClass<Key, T>(this Dictionary<Key, T> map, Key key) where T : ClassObject, new() { if (!map.TryGetValue(key, out T value)) { map.Add(key, CLASS(out value)); } return value; }

这类写法在事件系统、点击系统、资源系统里都很常见。

比如事件监听列表:

mGlobalListenerEventList.getOrAddClass(info.mEventTypeID).add(info);

这行代码背后做了三件事:

  • 根据事件类型查找监听列表
  • 如果没有,就从对象池创建一个
  • 然后把监听信息加进去

如果不封装,代码会变成一大段TryGetValue + CLASS + Add

现在一行就能表达清楚。

Dictionary 这类短写非常适合框架代码。

因为框架里经常会按 ID、类型、路径、状态分组。

这些分组逻辑如果每次都手写字典创建,代码会非常啰嗦。


四、字符串截取和处理短写

字符串截取也是项目里很常见的重复代码。

尤其是路径、文件名、配置字段、资源名、日志文本。

比如截取某个字符之前的内容。

原本写法:

int index = str.IndexOf('/'); if (index >= 0) { str = str[0..index]; }

现在写法:

str = str.rangeToFirst('/');

真实代码:

public static string rangeToFirst(this string str, char key) { if (str == null) { return str; } int endIndex = str.IndexOf(key); if (endIndex >= 0) { return str[0..endIndex]; } return str; }

截取某个字符之后的内容。

现在写法:

str = str.rangeFromFirst('/');

真实代码:

public static string rangeFromFirst(this string str, char key) { if (str == null) { return str; } int startIndex = str.IndexOf(key); if (startIndex < 0) { return str; } return str[(startIndex + 1)..]; }

截取两个字符之间的内容。

原本写法:

int startIndex = str.IndexOf('('); int endIndex = str.IndexOf(')'); if (startIndex >= 0 && endIndex >= 0 && endIndex > startIndex) { str = str[(startIndex + 1)..endIndex]; }

现在写法:

str = str.rangeBetweenKeyToKey('(', ')');

真实代码:

public static string rangeBetweenKeyToKey(this string str, char key0, char key1) { if (str == null) { return str; } int startIndex = str.IndexOf(key0); int endIndex = str.IndexOf(key1); if (startIndex < 0 || endIndex < 0 || endIndex < startIndex) { return str; } return str[(startIndex + 1)..endIndex]; }

移除前缀。

原本写法:

if (str != null && str.StartsWith(prefix)) { str = str[prefix.Length..]; }

现在写法:

str = str.removeStartString(prefix);

真实代码:

public static string removeStartString(this string str, string pattern, bool caseSensitive = true) { if (str == null || pattern == null || str.Length < pattern.Length) { return str; } bool needRemove; if (caseSensitive) { needRemove = str.StartsWith(pattern); } else { needRemove = str.ToLower().StartsWith(pattern.ToLower()); } if (needRemove) { return str[pattern.Length..]; } return str; }

移除后缀。

原本写法:

if (str != null && str.EndsWith(suffix)) { str = str[0..(str.Length - suffix.Length)]; }

现在写法:

str = str.removeEndString(suffix);

真实代码:

public static string removeEndString(this string str, string pattern, bool caseSensitive = true) { if (str == null || pattern == null || str.Length < pattern.Length) { return str; } bool needRemove; if (caseSensitive) { needRemove = str.EndsWith(pattern); } else { needRemove = str.ToLower().EndsWith(pattern.ToLower()); } if (needRemove) { return str[0..(str.Length - pattern.Length)]; } return str; }

确保前缀存在。

原本写法:

if (!path.StartsWith("Assets/")) { path = "Assets/" + path; }

现在写法:

path = path.ensurePrefix("Assets/");

真实代码:

public static string ensurePrefix(this string str, string prefix) { if (!str.StartsWith(prefix)) { return prefix + str; } return str; }

确保后缀存在。

原本写法:

if (!path.EndsWith("/")) { path += "/"; }

现在写法:

path = path.ensureSuffix("/");

真实代码:

public static string ensureSuffix(this string str, string prefix) { if (!str.EndsWith(prefix)) { return str + prefix; } return str; }

这类短写非常适合路径处理。

因为路径处理里经常要做这些事:

  • 去掉前缀
  • 去掉后缀
  • 截取文件夹
  • 截取文件名
  • 保证路径以/结尾
  • 保证路径以Assets/开头
  • 截取某个标记之间的内容

如果每个地方都写IndexOfSubstringStartsWithEndsWith,代码会非常散。

封装成字符串扩展以后,调用侧就更像自然语言。

比如:

path = path.ensureSuffix("/");

这行代码一眼就能看懂:

确保 path 有/后缀。

这就是代码简化的意义。


五、数组、Span、byte[] 操作短写

数组访问经常要做边界判断。

原本写法:

T value = default; if (array != null && index >= 0 && index < array.Length) { value = array[index]; }

现在写法:

T value = array.get(index);

真实代码:

public static T get<T>(this T[] list, int index) { if (list.isEmpty() || index < 0 || index >= list.Length) { return default; } return list[index]; }

数组设置也是一样。

原本写法:

if (array != null && index >= 0 && index < array.Length) { array[index] = value; }

现在写法:

array.set(index, value);

真实代码:

public static void set<T>(this T[] list, int index, T value) { if (list.isEmpty() || index < 0 || index >= list.Length) { return; } list[index] = value; }

取第一个元素。

原本写法:

T value = array == null || array.Length == 0 ? default : array[0];

现在写法:

T value = array.first();

真实代码:

public static T first<T>(this T[] list) { if (list.isEmpty()) { return default; } return list[0]; }

数组安全遍历。

原本写法:

if (array != null) { foreach (var item in array) { ... } }

现在写法:

foreach (var item in array.safe()) { ... }

真实代码:

public static T[] safe<T>(this T[] original) { return original ?? EmptyArray<T>.getEmptyList(); }

判断数组里是否包含某个值。

原本写法:

bool contains = false; if (array != null) { for (int i = 0; i < array.Length; ++i) { if (equal(array[i], value)) { contains = true; break; } } }

现在写法:

bool contains = array.contains(value);

真实代码:

public static bool contains<T>(this T[] list, T value) { if (list.isEmpty()) { return false; } int length = list.Length; for (int i = 0; i < length; ++i) { if (equal(list[i], value)) { return true; } } return false; }

byte[] 转字符串也可以短写。

原本写法:

string text = Encoding.UTF8.GetString(bytes);

现在写法:

string text = bytes.bytesToString();

真实代码:

public static string bytesToString(this byte[] bytes, Encoding encoding = null) { if (bytes.isEmpty()) { return string.Empty; } // 默认为UTF8 return removeLastZero((encoding ?? Encoding.UTF8).GetString(bytes)); }

Span<byte> 也可以保持同样写法:

string text = span.bytesToString();

真实代码:

public static string bytesToString(this Span<byte> bytes, Encoding encoding = null) { if (bytes == null) { return null; } if (bytes.Length == 0) { return string.Empty; } // 默认为UTF8 return removeLastZero((encoding ?? Encoding.UTF8).GetString(bytes)); }

这类短写解决的是两个问题。

第一,减少边界判断。

第二,统一常用转换。

数组访问如果每次都手写边界判断,代码会很长。

但如果不判断,又容易越界。

所以框架提供get()set()first()这种短写以后,调用侧更简单。

byte[] 转字符串也是同理。

项目里经常会有网络数据、文件数据、配置数据需要转字符串。

如果到处直接写Encoding.UTF8.GetString(bytes),还要考虑空数组、尾部 0、编码默认值等问题。

统一写成:

bytes.bytesToString();

调用侧就更干净。


总结

这一篇基于 MyFramework 最新版本,重新调整了 5 类真正能让调用侧变短的技巧:

  • 字符串和数字转换短写
  • List 操作短写
  • Dictionary 操作短写
  • 字符串截取和处理短写
  • 数组、Span、byte[] 操作短写

这一版里要特别注意,很多转换函数已经是扩展方法写法。

比如:

idString.SToI(); speedString.SToF(); id.IToS(); guid.LToS(); value.FToS(2); enable.boolToString(); pos.V3ToS(2); "0.5".toPercent(); 0.5f.toPercent();

它们的共同点是:

原本要写几行的固定模式,现在可以压成一行。

比如:

if (!list.Contains(value)) { list.Add(value); }

变成:

list.addUnique(value);

比如:

if (dic.TryGetValue(key, out int curValue)) { dic[key] = curValue + increase; } else { dic.Add(key, increase); }

变成:

dic.addOrIncreaseValue(key, increase);

比如:

int index = str.IndexOf('/'); if (index >= 0) { str = str[0..index]; }

变成:

str = str.rangeToFirst('/');

比如:

if (array != null && index >= 0 && index < array.Length) { value = array[index]; }

变成:

value = array.get(index);

这些不是为了隐藏业务逻辑。

它们隐藏的是每天都会重复出现的样板代码。

一句话总结:

代码简化不是少写业务。

而是不要让业务代码被重复的固定写法淹没。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/6 13:01:24

使用Let‘s Encrypt与Certbot为Nginx网站免费配置HTTPS及自动续期

1. 项目概述&#xff1a;为什么你的网站必须上HTTPS&#xff1f;最近在帮几个朋友部署个人博客和项目站点时&#xff0c;我发现一个挺普遍的现象&#xff1a;很多开发者&#xff0c;尤其是刚入行的朋友&#xff0c;对本地开发、后端逻辑、前端框架玩得很溜&#xff0c;但一到要…

作者头像 李华
网站建设 2026/7/6 12:58:30

AD 23 与 AutoCAD 2024 协同:DXF 板框导入的 5 项预处理与 1 个精度保障技巧

AD 23与AutoCAD 2024高效协同&#xff1a;DXF板框导入的预处理全流程与精度控制实战在硬件开发领域&#xff0c;PCB工程师与结构工程师的协同效率直接影响产品开发周期。当结构工程师使用AutoCAD 2024完成机箱设计后&#xff0c;如何确保其提供的DXF文件能够精准导入Altium Des…

作者头像 李华
网站建设 2026/7/6 12:51:55

如何通过Nucleus Co-Op实现单机游戏分屏多人协作

如何通过Nucleus Co-Op实现单机游戏分屏多人协作 【免费下载链接】nucleuscoop Starts multiple instances of a game for split-screen multiplayer gaming! 项目地址: https://gitcode.com/gh_mirrors/nu/nucleuscoop 在传统的游戏体验中&#xff0c;单机游戏往往意味…

作者头像 李华
网站建设 2026/7/6 12:50:48

从零到一:手把手教你训练专属YOLO模型并完成本地部署

你肯定遇到过这种情况&#xff1a;想用 AI 识别某个特定物体&#xff0c;比如自家果园里的成熟果子、工厂流水线上的瑕疵品&#xff0c;或者监控里某个特定行为。网上找的通用模型要么识别不准&#xff0c;要么压根没有你要的类别。这时候&#xff0c;一个念头就会冒出来&#…

作者头像 李华
网站建设 2026/7/6 12:49:02

图像增强频域滤波实战:Halcon FFT与3种滤波器实现缺陷凸显

频域滤波实战&#xff1a;Halcon FFT与3种滤波器实现工业缺陷检测在工业视觉检测领域&#xff0c;LCD Mura缺陷和织物瑕疵这类具有周期性纹理的背景中识别细微异常&#xff0c;传统空间域方法往往力不从心。频域分析通过傅里叶变换将图像转换到频率维度&#xff0c;让隐藏在复杂…

作者头像 李华