ref return 和 ref local

Ref 返回和 ref locals 对于操作和返回对内存块的引用非常有用,而不是在不诉诸不安全指针的情况下复制内存。

参考返回

public static ref TValue Choose<TValue>(
    Func<bool> condition, ref TValue left, ref TValue right)
{
    return condition() ? ref left : ref right;
}

有了这个,你可以通过引用传递两个值,其中一个值根据某些条件返回:

Matrix3D left = …, right = …;
Choose(chooser, ref left, ref right).M20 = 1.0;

参考本地

public static ref int Max(ref int first, ref int second, ref int third)
{
    ref int max = first > second ? ref first : ref second;
    return max > third ? ref max : ref third;
}
…
int a = 1, b = 2, c = 3;
Max(ref a, ref b, ref c) = 4;
Debug.Assert(a == 1); // true
Debug.Assert(b == 2); // true
Debug.Assert(c == 4); // true

不安全的参考操作

System.Runtime.CompilerServices.Unsafe 中,已经定义了一组不安全的操作,它们允许你操作 ref 值,就好像它们是指针一样。

例如,将内存地址(ref)重新解释为不同的类型:

byte[] b = new byte[4] { 0x42, 0x42, 0x42, 0x42 };

ref int r = ref Unsafe.As<byte, int>(ref b[0]);
Assert.Equal(0x42424242, r);

0x0EF00EF0;
Assert.Equal(0xFE, b[0] | b[1] | b[2] | b[3]);

但是,在执行此操作时请注意字节顺序 ,例如,如果需要,请检查 BitConverter.IsLittleEndian 并相应地处理。

或者以不安全的方式迭代数组:

int[] a = new int[] { 0x123, 0x234, 0x345, 0x456 };

ref int r1 = ref Unsafe.Add(ref a[0], 1);
Assert.Equal(0x234, r1);

ref int r2 = ref Unsafe.Add(ref r1, 2);
Assert.Equal(0x456, r2);

ref int r3 = ref Unsafe.Add(ref r2, -3);
Assert.Equal(0x123, r3);

或类似的 Subtract

string[] a = new string[] { "abc", "def", "ghi", "jkl" };

ref string r1 = ref Unsafe.Subtract(ref a[0], -2);
Assert.Equal("ghi", r1);

ref string r2 = ref Unsafe.Subtract(ref r1, -1);
Assert.Equal("jkl", r2);

ref string r3 = ref Unsafe.Subtract(ref r2, 3);
Assert.Equal("abc", r3);

另外,可以检查两个 ref 值是否相同,即相同的地址:

long[] a = new long[2];

Assert.True(Unsafe.AreSame(ref a[0], ref a[0]));
Assert.False(Unsafe.AreSame(ref a[0], ref a[1]));

链接

Roslyn Github 问题

github 上的 System.Runtime.CompilerServices.Unsafe