Как изменить адрес массива в C # небезопасно - PullRequest
0 голосов
/ 26 июня 2019

У меня есть массив структур, которые мне нужно преобразовать в массив целых, без необходимости вручную копировать его.

Я пытался получить адрес массива, но безрезультатно.

Это эквивалент C ++ того, что я пытаюсь сделать:

struct MyStruct
{
    public int a;
    public int b;
} 
MyStruct* structs = new MyStruct[3];
int* ints;

ints = (int*)structs; //works

Это код:

MyStruct[] structs = new MyStruct[3];
int[] ints = new int[6];

ints = (int[])structs; //error

Я тоже пробовал это:

MyStruct[] structs = new MyStruct[3];
int[] ints = new int[6];

fixed(int* ptr = ints)
{
    ptr = (int*)&structs; //error
}

Похоже, что независимо от того, что я пытаюсь, я не могу изменить указатель массива. Я могу прочитать это, но я не могу изменить это. есть ли способ?

1 Ответ

0 голосов
/ 26 июня 2019

Вместо того чтобы выполнять небезопасное приведение, подумайте о реализации оболочки для массива: пользовательский класс с индексатором, который преобразует часть внутреннего массива в структуру и наоборот. Взгляните на этот класс:

public class ArrayWrapper<T> where T : struct
{
    public ArrayWrapper(int structsCount)
    {
        var fields = typeof(T)
            .GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);

        // do some check
        if (fields.Any(f => f.FieldType != typeof(int)))
            throw new Exception("Only int fields allowed");
        if (fields.Any(f => f.IsPrivate))
            throw new Exception("Only public fields allowed");

        InnerArray = new int[structsCount * fields.Length];

        copyFromArrayToStruct = CopyFromArrayToStructMethodBuilder(fields);
        copyFromStructToArray = CopyFromStructToArrayMethodBuilder(fields);
    }

    public int[] InnerArray { get; }

    private readonly Func<int, T> copyFromArrayToStruct;
    private readonly Action<int, T> copyFromStructToArray;
    public T this[int index]
    {
        get => copyFromArrayToStruct(index);
        set => copyFromStructToArray(index, value);
    }

    private Func<int, T> CopyFromArrayToStructMethodBuilder(FieldInfo[] fields)
    {
        var index = Expression.Parameter(typeof(int), "i");

        var actualOffset = Expression.Multiply(index, Expression.Constant(fields.Length));
        var result = Expression.Variable(typeof(T), "result");
        var lines = new List<Expression> {Expression.Assign(result, Expression.New(typeof(T)))};

        for (var i = 0; i < fields.Length; ++i)
        {
            var fieldInfo = fields[i];
            var left = Expression.Field(result, fieldInfo);
            var right = Expression.ArrayIndex(
                Expression.Constant(InnerArray),
                Expression.Add(actualOffset, Expression.Constant(i)));

            var assignment = Expression.Assign(left, right);
            lines.Add(assignment);
        }

        lines.Add(result);

        var lambda = Expression.Lambda<Func<int, T>>(Expression.Block(new []{result}, lines), index);
        return lambda.Compile();
    }

    private Action<int, T> CopyFromStructToArrayMethodBuilder(FieldInfo[] fields)
    {
        var index = Expression.Parameter(typeof(int), "i");
        var value = Expression.Parameter(typeof(T), "value");

        var actualOffset = Expression.Multiply(index, Expression.Constant(fields.Length));
        var lines = new List<Expression>();

        for (var i = 0; i < fields.Length; ++i)
        {
            var fieldInfo = fields[i];
            var left = Expression.ArrayAccess(Expression.Constant(InnerArray),
                Expression.Add(actualOffset, Expression.Constant(i)));
            var right = Expression.Field(value, fieldInfo);

            var assignment = Expression.Assign(left, right);
            lines.Add(assignment);
        }

        var lambda = Expression.Lambda<Action<int, T>>(Expression.Block(lines), index, value);
        return lambda.Compile();
    }
}

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

var array = new ArrayWrapper<MyStruct>(4);

var test = array[0]; // get part of array as a structure
test.a = 10;
test.b = 42;
array[0] = test; // put modified data back to array

Эта обертка фактически копирует данные, но только для тех частей массива, которые вы действительно хотите изменить. Также это может быть упрощено, если вы предпочитаете жестко программировать поля структуры, а не использовать генерацию кода. Вот вживую демо

...