学习c# 7.0-7.3的ref、fixed特性并在Unity下测试

1.ref的一些运用

1.1 ref readonly

关于ref,一个主要应用是防止结构体拷贝,若返回的结构体不需要修改则用ref readonly,类似c++的const标记 :

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。

(也可以参考ZString(https://github.com/Cysharp/ZString),把相关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、指针这些内容。以上就是对这几样新功能的可用范围进行思考并编写的一些

小案例。

posted @ 2021-10-19 21:28  HONT  阅读(328)  评论(0编辑  收藏  举报