1.ref的一些运用
1.1 ref readonly
关于ref,一个主要应用是防止结构体拷贝,若返回的结构体不需要修改则用ref readonly:
private ref readonly Attr PlayerSetting(Player player) { return ref player.attr; }
1.2 array ref
由于索引器不支持ref,所以目前只有数组的元素可以用ref:
int[] arr = new[] {1, 2, 3}; ref int item = ref arr[0]; //可用 //List<int> list = new List<int>(); //ref int item = ref list[0]; //报错
1.3 属性ref(返回值ref)
可以借由ref返回值的支持,给属性加上该关键字,以提示使用者优先考虑用ref方法读写
public class Player { private int mHp; public ref int Hp => ref mHp; }
1.4 多值ref
目前返回值不支持多ref返回,解构功能也不支持ref,但由于委托参数支持ref,因此可间接实现多字段ref编辑
使用:
RefStructTest refStructTest = new RefStructTest(); refStructTest.SetValues((ref int x, ref float y, ref float z, ref bool w) => { x = 24; y = 12.0f; z = 6.0f; w = true; });
定义:
public delegate void RefTestValueSet(ref int x, ref float y, ref float z, ref bool w); public struct RefStructTest { public int a; public float b; public float c; public bool d; public void SetValues(RefTestValueSet set) { set(ref a, ref b, ref c, ref d); } }
2.struct fixed的一些运用
fixed关键字可以固定内存地址,从而使用指针访问该地址,struct中的fixed字段可以实现struct内直接包含数组,
而不是链接到堆内存的数组。struct微软建议是将大小控制在16字节(后来也有24字节说法)以内,虽然可以配合ref做到不频繁拷贝,
但目前还没有明确的资料确定不会产生性能影响。
官方文档的fixed页面里也没有看见,所以谨慎使用吧。
通常指针和栈集合的分配可以使用spin,但unity目前没有集成spin以及对应的dll。
fixed本身并不是一个新功能,但在7.x版本后对其进行了增强。
unity可以通过AssemblyDefinition实现局部的unsafe code功能,下面的一些unsafe特性代码也是定义
在一个unsafe库中。
2.1 一个理论上可以实现的栈数组
public unsafe struct StructArray { private fixed int arr[8]; public void SetArr(int index, int value) { arr[index] = value; } public ref int GetArrValue(int index) { return ref arr[index]; } }
通过函数接口的调用,外部代码不需要访问指针即可在栈中使用数组,
简单的测试一下:
StructArray structArray = new StructArray(); structArray.SetArr(0, 10); structArray.SetArr(1, 20); structArray.SetArr(2, 30); Debug.Log(structArray.GetArrValue(0));//10 Debug.Log(structArray.GetArrValue(1));//20 Debug.Log(structArray.GetArrValue(2));//30
2.2 一个简单的栈string结构
因为string是个分配在堆上的结构,且修改会重新创建,有性能开销。
因此有许多优化string的方案,这里我们可以分配栈上的char[]来做一些优化(测试代码,只支持8个字符):
using System; public unsafe struct StructString { private fixed char arr[8]; public void SetString(string str) { fixed (char* dstPtr = arr) { fixed (char* srcPtr = str) { Buffer.MemoryCopy(srcPtr, dstPtr, 16L, 16L); } } } public string GetString() { string result = string.Empty; fixed (char* ptr = arr) { result = new string(ptr); } return result; } public bool EqulasCheck(StructString other) { bool result = true; fixed (char* xPtr = &arr[0])//16字节,只需要做2次long类型判断 { char* yPtr = other.arr; if (*(long*) xPtr != *(long*) yPtr) { result = false; } else if (*(long*) (xPtr + 4) != *(long*) (yPtr + 4)) { result = false; } } return result; } }
使用:
StructString str = new StructString(); str.SetString("qwe");//qwe Debug.Log(str.GetString()); StructString str2 = new StructString(); str2.SetString("qwe");//true Debug.Log(str.EqulasCheck(str2));
可以看见,封装之后外部代码可以不接触unsafe、指针这些内容。以上就是对这几样新功能的可用范围进行思考并编写的一些
小案例。