I need to generate code using Expression trees that quickly fills out an array of structs T[]
where T contains a readonly field. I need to initialize it like after GetUninitializedObject() + IL or reflection-based setters.
UPDATE: At the moment it appears to be impossible. Please vote for it at MS Suggestions
struct Strct
{
public readonly int Value;
}
this code fails:
Expression.Assign(
Expression.Field(structByIndexFromArrayExp, "Value"),
deserializedValueExp)
During the expression tree construction, I get this error: Expression must be writeable
Which totally makes sense from the regular code perspective, but not during deserialization.
FormatterServices.GetUninitializedObject()
returns an object, which I would guess I need to avoid as it is boxed and therefore significantly slower.
What is the quickest way to initialize such struct arrays?
Update: At the moment the only realistic way I see is to dynamically generate a clone of struct T but without readonly attribute on fields, fill them out, fix both arrays in memory and do a memory copying. Please vote to tell Microsoft to fix it.
Just because you're deserializing doesn't mean you get to break the rules of the language. The compiler complains if I try this:
void Main()
{
var a = new Foo{Bar = 1};
}
public struct Foo
{
public readonly int Bar;
}
Expression trees can't be expected to perform actions that you can't perform in code. If the property shouldn't really be readonly
, remove the readonly
keyword. Otherwise, you should have a constructor that allows you to initialize it.
public struct Foo
{
public Foo(int bar) {this.Bar = bar;}
public readonly int Bar;
}
Then create an expression that calls that constructor rather than trying to set the field directly.
There actually is a workaround since you can use Expressions to call reflection methods. Only be aware that this is a lot slower.
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);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With