Элементы записи и получения в массиве указателей .NET 4.7.3 - PullRequest
0 голосов
/ 04 июня 2018

Я пытаюсь создать массив указателей, чтобы можно было отследить ссылку на любой неуправляемый объект, добавленный к нему, и изменить его, но поведение отличается, когда я создаю массив указателей Int и когда создать массив указателей Persons что неуправляемо.

    public unsafe struct Person
    {
        public char* name;
        public int age;

        public Person(string name, int age)
        {
            this.name = (char*)Marshal.StringToCoTaskMemAuto(name);
            this.age = age;
        }
    }

для добавления:

public static unsafe void WriteValueAtPointerArrayIndex<T>(ref T** pointerArray, int index, in T value) where T : unmanaged
    {
        T** ptr = pointerArray;
        fixed (T* ptrValue = &value)
        {
            ptr += index;
            *ptr = ptrValue;

            Console.WriteLine("Address where add: {0}, Value: {1}", (long)ptr, **ptr);
        }
    }

для получения:

public static unsafe ref T* GetPointerValueAtPointerArrayIndex<T>(ref T** pointerArray, int index) where T : unmanaged
    {
        T** ptr = pointerArray;
        ptr += index;

        ref T* value = ref *ptr;
        Console.WriteLine("Address to return: {0}, Value {1}", (long)ptr, *value);
        return ref value;
    }

Используя методы:

UnsafeList использует методы внутри и создает внутренний массив, используя:

 T**_Array = (T**)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * _Capacity);

, где T: неуправляемый (новая функция .NET 4.7.3)

        UnsafeList<int> list = new UnsafeList<int>(10);
        list.Add(10);
        list.Add(14);
        list.Add(20);
        list.Add(25);
        list.Add(30);

        for(int i = 0; i < list.Length; i++)
        {
            var v = list[i];
        }

Использование с персоной:

        UnsafeList<Person> persons = new UnsafeList<Person>(10);
        Person miguel = new Person("Miguel", 23);
        Person elena = new Person("Elena", 24);
        Person ana = new Person("Ana", 34);
        Person ulises = new Person("Ulises", 23);


        persons.Add(miguel);
        persons.Add(elena);
        persons.Add(ana);
        persons.Add(ulises);

        for (int i = 0; i < persons.Length; i++)
        {
            var v = persons[i];
        }

Результаты:

  • Для целых:

    Address where add: 2151492103952, Value: 10    
    Address where add: 2151492103960, Value: 14    
    Address where add: 2151492103968, Value: 20    
    Address where add: 2151492103976, Value: 25    
    Address where add: 2151492103984, Value: 30
    

/// Здесь возвращается только последний добавленный, но все адреса верны

    Address to return: 2151492103952, Value 30
    Address to return: 2151492103960, Value 30
    Address to return: 2151492103968, Value 30
    Address to return: 2151492103976, Value 30
    Address to return: 2151492103984, Value 30
  • Для лиц:

    Address where add: 2151492060192, Value: Miguel, 23
    Address where add: 2151492060200, Value: Elena, 24
    Address where add: 2151492060208, Value: Ana, 34
    Address where add: 2151492060216, Value: Ulises, 23
    
    Address to return: 2151492060192, Value Miguel, 23
    Address to return: 2151492060200, Value Elena, 24
    Address to return: 2151492060208, Value Ana, 34
    Address to return: 2151492060216, Value Ulises, 23
    

Почемурезультаты не согласуются при использовании Int32?Я новичок в c # указателях.

1 Ответ

0 голосов
/ 04 июня 2018

ммммм нашел проблему.Ваш код не имеет никакого смысла.

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); 
...