Как инициализировать структуру в массиве со значениями только для чтения, используя деревья выражений в C # - PullRequest
4 голосов
/ 29 марта 2012

Мне нужно сгенерировать код, используя деревья выражений, которые быстро заполняют массив структур T[], где T содержит поле только для чтения. Мне нужно инициализировать его, как после GetUninitializedObject () + IL или сеттеров на основе отражения.

ОБНОВЛЕНИЕ: В настоящее время это кажется невозможным. Пожалуйста, проголосуйте за это на MS Applications

struct Strct
{
    public readonly int Value;
}

этот код не работает:

Expression.Assign(
    Expression.Field(structByIndexFromArrayExp, "Value"),
    deserializedValueExp)

Во время построения дерева выражений я получаю эту ошибку: Expression must be writeable Что вполне логично с точки зрения обычного кода, но не во время десериализации.

FormatterServices.GetUninitializedObject() возвращает объект, который, я думаю, мне нужно избегать, поскольку он упакован и поэтому значительно медленнее.

Какой самый быстрый способ инициализации таких структурных массивов?

Обновление: На данный момент единственный реалистичный способ, который я вижу, - это динамически генерировать клон структуры T, но без атрибута readonly для полей, заполнять их, исправлять оба массива в памяти и выполнять копирование памяти , Пожалуйста, проголосуйте, чтобы сказать Microsoft, чтобы исправить это .

Ответы [ 2 ]

6 голосов
/ 29 марта 2012

То, что вы десериализуете, не означает, что вы нарушаете правила языка.Компилятор жалуется, если я пытаюсь это сделать:

void Main()
{
    var a = new Foo{Bar = 1};
}

public struct Foo
{
    public readonly int Bar;
}

Нельзя ожидать, что деревья выражений будут выполнять действия, которые вы не можете выполнить в коде.Если свойство не должно быть readonly, удалите ключевое слово readonly.В противном случае у вас должен быть конструктор, который позволяет вам его инициализировать.

public struct Foo
{
    public Foo(int bar) {this.Bar = bar;}
    public readonly int Bar;
}

Затем создайте выражение, которое вызывает этот конструктор, а не пытайтесь установить поле напрямую.

2 голосов
/ 20 марта 2014

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

public static Expression CreateSetValueExpression(Expression target, Expression value, FieldInfo fieldInfo)
{
    // workaround for readonly fields: use reflection, this is a lot slower but the only way except using il directly
    if (fieldInfo.IsInitOnly)
    {
        MethodInfo fieldInfoSetValueMethod = typeof(FieldInfo).GetMethod("SetValue", new[] { typeof(object), typeof(object) }); 
        return Expression.Call(Expression.Constant(fieldInfo), fieldInfoSetValueMethod, target, Expression.Convert(value, typeof(object)));
    }

    return Expression.Assign(Expression.Field(target, fieldInfo), value);
}
...