【问题标题】:Write and Get elements in array of pointers .NET 4.7.3在指针数组中写入和获取元素 .NET 4.7.3
【发布时间】:2018-11-14 00:10:15
【问题描述】:

我正在尝试创建一个指针数组,以便能够跟踪对添加到其中的任何非管理对象的引用并对其进行更改,但是当我创建 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# 指针的新手。

【问题讨论】:

  • 您没有向我们展示 Add 方法。也许错误就在那里。
  • Add 方法只是 public void Add(in T value),它调用 WriteValueAtPointerArrayIndex(...)

标签: c# pointers marshalling unmanaged .net-4.7.2


【解决方案1】:

嗯嗯发现了问题。你的代码没有任何意义。

fixed (T* ptrValue = &value)

然后

*ptr = ptrValue;

您正在保存所传递数据的地址。如果传递的数据存在于堆栈中,则这是错误。堆栈上的值是瞬态的。你对他们的生命没有太多的控制权。现在... Person 的示例... 您在堆栈上创建了四个Persons。 C# 编译器的生命周期可能会持续到方法结束。因此,您将他们的地址添加到您的UnsafeList&lt;&gt;,然后当您查看UnsafeList&lt;&gt; 时,他们仍然在那里。但是,例如,如果您尝试从方法中使用return,则可能带有Persons 的堆栈将被清除/覆盖,或者至少将不再归您所有。第一个示例(不工作的示例)更糟糕:通过在.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

如何更正您的代码?甚至不清楚您要做什么,但是您肯定做错了! :-)

一些未经测试的代码!!我什至没有阅读有关refin 的文档! :-)

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

【讨论】:

  • 谢谢,第一次使用指针,我试图弄清楚如何改进我的代码。你说的是正确的,我发现解决这个问题的方法是使用GCHandle.Alloc(obj, GCHandleType.Pinned),但是如果传递的对象已经被固定,我不知道是否有可能知道一个对象是否固定在内存中。 GC 如何不移动结构我想当您将其分配给变量时将其固定。
  • 我要做的是一个自定义列表,它存储对添加到其中的对象的引用。每次添加一个新的非托管对象时,都会创建一个指向该对象的指针,这样我就可以检索/修改任何添加的对象,因为我可以直接引用它。这是为了避免每次传递时都会复制结构。
  • @FreddxL。您不能固定值类型。您只能固定引用类型。而且您尝试做的事情没有多大意义,因为值类型通常存在于堆栈中,因此它们的生命周期很短。或者,它们可以存在于数组中,或者它们可以作为引用类型的字段存在(在这种情况下,可以移动引用类型)。
  • 那么,要将 ValueType 存储在我的指针数组中,必须使用 Marshall.AllocHGlobal(...) 将其存储在堆上分配?我想要的是存储一个指向添加到 List 数组中的任何对象的指针,然后能够使用它们的地址访问这些对象,所以我需要 GC 不要移动这些对象。
  • @FreddxL。您甚至可以使用数组...您可以引用数组的元素。仍然不清楚您要做什么。您想要一个具有 ref 索引器的集合吗?然后使用 ref 索引器构建一个集合。我正在添加一段代码作为示例。不清楚它是否有效。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-12-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多