ммммм нашел проблему.Ваш код не имеет никакого смысла.
fixed (T* ptrValue = &value)
, а затем
*ptr = ptrValue;
Вы сохраняете адрес переданных данных.Это неправильно , если переданные данные живут в стеке.Значения в стеке являются временными.Вы не имеете большого контроля над их жизнью.Теперь ... Пример с Person
... Вы создаете четыре Person
s в стеке.Компилятор C #, вероятно, делает их время жизни до конца метода.Таким образом, вы добавляете их адрес к вашему UnsafeList<>
, а затем, когда вы смотрите на UnsafeList<>
, они все еще там.Но если, например, вы попытаетесь return
из метода, вероятно, часть стека с Person
будет очищена / перезаписана или, по крайней мере, больше не будет принадлежать вам.Первый пример (тот, который не работает) еще хуже: используя константы в .Add()
, константа помещается в стек перед вызовом, берется адрес для этого стека, а затем константа извлекается изстек в конце метода.Затем новая константа отправляется по тому же адресу, метод вызывается снова, и константа извлекается.Таким образом, в конце вы сохраняете несколько копий одного и того же адреса, который будет указывать на последнее использованное значение константы.Чтобы выполнить тест, попробуйте напечатать ptrValue
!
. Чтобы дать ясный и простой пример:
public static unsafe void Test<T>(in T value) where T : unmanaged
{
fixed (T* ptr = &value)
{
Console.WriteLine($"Ptr: {(IntPtr)ptr}, Value: {value}");
}
}
, а затем:
Test(1);
Test(2);
Test(3);
Test(4);
Test(5);
Console.WriteLine();
int a1 = 1, a2 = 2, a3 = 3, a4 = 4, a5 = 5;
Test(a1);
Test(a2);
Test(a3);
Test(a4);
Test(a5);
Результат:
Ptr: 97774252, Value: 1
Ptr: 97774252, Value: 2
Ptr: 97774252, Value: 3
Ptr: 97774252, Value: 4
Ptr: 97774252, Value: 5
Ptr: 97774340, Value: 1
Ptr: 97774336, Value: 2
Ptr: 97774332, Value: 3
Ptr: 97774328, Value: 4
Ptr: 97774324, Value: 5
Как исправить свой код?Даже не ясно, что вы пытаетесь сделать, но, конечно, вы делаете это неправильно!: -)
Какой-то непроверенный код !!Я даже не читал документацию о ref
и in
!: -)
public class RefList<T>
{
private T[] _array = new T[16];
public int Capacity { get => _array.Length; }
public int Length { get; private set; }
// TODO Range checks ix >= 0 && ix < Length
public ref T this[int ix] { get => ref _array[ix]; }
public ref T AddEmpty()
{
Length++;
if (Length == _array.Length)
{
// This will invalidate all the ref that
// have been returned until now!
Array.Resize(ref _array, _array.Length * 2);
}
return ref _array[Length - 1];
}
public ref T Add(in T value)
{
ref T el = ref AddEmpty();
// Note that value is *copied* to el!
el = value;
return ref el;
}
}
, затем
public struct MyStruct
{
public int A;
}
, затем
var rl = new RefList<MyStruct>();
ref var ms = ref rl.AddEmpty();
ms.A = 5;
Console.WriteLine(rl[0].A);
ms = new MyStruct { A = 10 };
Console.WriteLine(rl[0].A);