Возможен ли диапазон <T>, указывающий на буферы фиксированного размера без фиксированного выражения? - PullRequest
0 голосов
/ 21 января 2019

Я использую .NET Core 2.1 и языковой стандарт 7.3.Я хотел бы сослаться на фиксированный буфер без получения указателя на него.Возможно ли это в настоящее время?

public unsafe struct InteropStruct
{
    private fixed byte dataField[32];

    public Span<byte> Data
    {
        get
        {
            //return a span referencing the private field without a fixed statement
        }
    }
}

Я знаю, что Span в настоящее время может отслеживать управляемый массив с помощью сборщиков мусора, поэтому я не вижу ничего, что мешало бы ему аналогичным образом отслеживать фиксированные буферы.

Если это невозможно, что произойдет, если я использую фиксированный оператор, подобный следующему:

public unsafe struct InteropStruct
{
    private fixed byte dataField[32];

    public Span<byte> Data
    {
        get
        {
            fixed (byte* ptr = dataField)
            {
                return new Span<byte>(ptr, 32);
            }
        }
    }
}

Не станет ли сборщик мусора проблемой, если структура будет помещена в объект или класс в куче

1 Ответ

0 голосов
/ 23 января 2019

Итак, я провел некоторые исследования в виде сборок ILSpy .NET и некоторое тестирование на .NET Core 2.1.Результаты моего теста следующие:

    interface ITest
    {
        Span<byte> Data { get; }
    }

    unsafe struct TestStruct : ITest
    {
        fixed byte dataField[8];

        public Span<byte> Data
        {
            get
            {
                //Unsafe.AsPointer() to avoid the fixed expression :-)
                return new Span<byte>(Unsafe.AsPointer(ref dataField[0]), 8);
            }
        }
    }

    class Program
    {
        //Note: This test is done in Debug mode to make sure the string allocation isn't ommited
        static void Main(string[] args)
        {
            new string('c', 200);

            //Boxes the struct onto the heap.
            //The object is allocated after the string to ensure it will be moved during GC compacting
            ITest HeapAlloc = new TestStruct();

            Span<byte> span1, span2;

            span1 = HeapAlloc.Data; //Creates span to old location

            GC.Collect(2, GCCollectionMode.Forced, true, true); //Force a compacting garbage collection

            span2 = HeapAlloc.Data; //Creates span to new location

            //Ensures that if the pointer to span1 wasn't updated, that there wouldn't be heap corruption
            //Write to Span2
            span2[0] = 5;
            //Read from Span1
            Console.WriteLine(span1[0] == 5); //Prints true in .NET Core 2.1, span1's pointer is updated
        }
    }

Что я узнал из моего исследования IL, пожалуйста, прости меня, если я не правильно объясняю это:

.NET Core 2Span Field Span:

//Note, this is not the complete declaration, just the fields
public ref readonly struct Span<T>
{
    internal readonly ByReference<T> _pointer;
    private readonly int _length;
}

.NET Framework 3 Field Span:

//Same note as 2 Field Span
public ref readonly struct Span<T>
{
    private readonly Pinnable<T> _pinnable;
    private readonly IntPtr _byteOffset;
    private readonly int _length;
}

.Net Core использует 2-полевую модель Span.Из-за .NET Framework, использующего модель 3 полей, его указатель не будет обновлен.Причина?Конструктор Span<T>(void* pointer, int length) (который я использую для этого) для диапазона 3 полей устанавливает поле _byteOffset с аргументом pointer.Указатель в диапазоне 3 полей, который будет обновлен GC, является полем _pinnable.С 2 полями Span они одинаковы.

Итак, ответ на мой вопрос: да, у меня может быть точка Span на фиксированный буфер с фиксированным оператором или без него, но это опасно делатьвообще, когда не используется .NET Core 2 Span Model.Поправьте меня, если я ошибаюсь в текущей модели Span .NET Framework.

...