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