使用Bridge.Net编译C#时,避免小堆分配的点线框数值结构选型
嘿,针对你用Bridge.Net写C#处理图形元素时的数值结构需求,我有几个非常贴合场景的方案——完美避开元组的局限性,也不用碰复杂反射,还能满足循环遍历的要求:
1. 带索引器+可枚举的自定义结构体(最优解)
自定义结构体绝对是你的首选,轻量、强类型,而且Bridge.Net编译到JS后会转化为高效的轻量对象,完全没有额外开销。关键是给结构体加上索引器和IEnumerable实现,就能轻松支持循环遍历了。
比如处理二维点的结构体:
public struct Point : IEnumerable<float> { public float X { get; } public float Y { get; } public Point(float x, float y) { X = x; Y = y; } // 索引器,支持按位置直接访问,方便循环 public float this[int index] { get { return index switch { 0 => X, 1 => Y, _ => throw new IndexOutOfRangeException("Point only has 2 elements") }; } } // 实现IEnumerable,直接支持foreach循环 public IEnumerator<float> GetEnumerator() { yield return X; yield return Y; } // 显式实现非泛型枚举接口 IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }
再比如处理矩形的四元组结构体:
public struct Rect : IEnumerable<float> { public float Left { get; } public float Top { get; } public float Right { get; } public float Bottom { get; } public Rect(float left, float top, float right, float bottom) { Left = left; Top = top; Right = right; Bottom = bottom; } public float this[int index] { get { return index switch { 0 => Left, 1 => Top, 2 => Right, 3 => Bottom, _ => throw new IndexOutOfRangeException("Rect only has 4 elements") }; } } public IEnumerator<float> GetEnumerator() { yield return Left; yield return Top; yield return Right; yield return Bottom; } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); }
为什么这个方案适合你?
- 强类型语义:字段名(X/Y、Left/Top)比元组的Item1/Item2清晰太多,代码可读性拉满
- 原生支持遍历:不管是
foreach (var val in point)还是for (int i=0; i<2; i++) { var val = point[i]; }都能直接用 - Bridge.Net友好:完全不需要反射,编译到JS后逻辑直接映射,性能和手写JS对象差不多
- 值类型优势:结构体是值类型,传递时不会有引用类型的额外开销,适合频繁操作的图形元素
2. 内置数组+封装属性(简洁替代方案)
如果觉得写枚举器有点繁琐,也可以在结构体里内置一个私有数组,对外封装语义化属性,同时复用数组的枚举能力:
public struct Point : IEnumerable<float> { private readonly float[] _values; public float X => _values[0]; public float Y => _values[1]; public Point(float x, float y) { _values = new[] { x, y }; } public float this[int index] => _values[index]; // 直接复用数组的枚举器 public IEnumerator<float> GetEnumerator() => ((IEnumerable<float>)_values).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _values.GetEnumerator(); }
这个方案代码更简洁,对于2/4个元素的固定数组,Bridge.Net编译后的开销几乎可以忽略,适合追求代码简洁的场景。
3. 避坑小贴士
- 别碰反射:哪怕Bridge.Net支持部分反射API,编译到JS后性能也会大打折扣,完全没必要用它来遍历字段
- 优先不可变结构体:把字段设为
readonly,属性设为只读,避免值类型的意外副作用,Bridge.Net对不可变结构体的编译优化也更好 - 提供修改方法:如果需要修改数值,别直接改字段(不可变的话也改不了),提供类似
WithX的方法返回新结构体:public Point WithX(float newX) => new Point(newX, Y); public Point WithY(float newY) => new Point(X, newY);
总的来说,带索引器和可枚举的自定义结构体是最适配你场景的方案——兼顾了可读性、可操作性和Bridge.Net的编译效率,完美解决点、线、框这类图形元素的数值处理需求。
内容的提问来源于stack exchange,提问作者dukc
相关产品推荐
相关产品推荐

