I looked up for something similar, but I couldn't find anything that I am able to comprehend at the moment. I never needed to use Expressions so I don't really understand how they work, although they do look interesting. I can spend time studying them, but currently I am using them just for one single purpose, which is not really what they have been designed for, although I may be wrong here. It's to generate a run time function to set a value in to a FieldInfo of a class instance.
I would have done it with opcodes, which I understand better, but opcodes are not available on all the platforms I need to work with (i.e.: UWP). With NetStandard 2.0 I may actually use them, but before I try it, I wonder if you can tell me if this is possible. Currently I am using this code:
public static CastedAction<T> MakeSetter(FieldInfo field)
{
if (field.FieldType.IsInterfaceEx() == true && field.FieldType.IsValueTypeEx() == false)
{
ParameterExpression targetExp = Expression.Parameter(typeof(T), "target");
ParameterExpression valueExp = Expression.Parameter(typeof(object), "value");
MemberExpression fieldExp = Expression.Field(targetExp, field);
UnaryExpression convertedExp = Expression.TypeAs(valueExp, field.FieldType);
BinaryExpression assignExp = Expression.Assign(fieldExp, convertedExp);
Type type = typeof(Action<,>).MakeGenericType(typeof(T), typeof(object));
var setter = Expression.Lambda(type, assignExp, targetExp, valueExp).Compile();
return new CastedAction<T>(setter);
}
throw new ArgumentException();
}
}
public class CastedAction<T>
{
readonly Action<T, object> setter;
public CastedAction(Delegate setter)
{
this.setter = (Action<T, object>)setter;
}
public CastedAction(Action<T, object> setter)
{
this.setter = setter;
}
public void Call(ref T target, object value)
{
setter(target, value); //I want to pass ref target here
//target = setter(target, value); may be an alternative
}
However nowe I want to support structs as well and what I would like to have is to pass the first parameter in the setter Action by ref. As far as I understood this is not possible.
Therefore I was thinking to generate a Func returning the modified object passed by parameter. Here I got totally lost, too hard for me.
Even tho you may find a solution for this, I may still think to switch 100% op code as returning and passing by value could affect the performance of my application.
Amazingly, this simplified pass-by-ref Expression
example with a custom delegate type works... on full framework. I'm not at all sure that it will work the same way on a platform without full Compile()
, because you can't really represent a ref T
anywhere other than the stack, but ... worth a try:
using System;
using System.Linq.Expressions;
using System.Runtime.Serialization;
delegate string ByRefFunc<T>(ref T val);
struct X
{
public X(string name) => Name = name;
public string Name { get; }
}
static class P
{
static void Main()
{
var p = Expression.Parameter(typeof(X).MakeByRefType(), "p");
var lambda = Expression.Lambda<ByRefFunc<X>>(
Expression.Property(p, "Name"), p);
X x = new X("abc");
var s = lambda.Compile()(ref x);
Console.WriteLine(s);
}
}
Note that a struct
copy (because of missing ref
support) isn't the end of the world, unless you have huge struct
s.