Expression.Lambda 生成具有 return 值的函数

Expression.Lambda to generate a function with return value

我查找了类似的内容,但目前找不到任何我能够理解的内容。我从来不需要使用表达式,所以我并不真正理解它们是如何工作的,尽管它们看起来确实很有趣。我可以花时间研究它们,但目前我只是为了一个目的使用它们,这并不是它们的设计目的,尽管我在这里可能是错的。它是生成一个 运行 时间函数以将值设置到 class 实例的 FieldInfo 中。

我会用操作码来完成它,我更了解它,但操作码并不是在我需要使用的所有平台上都可用(即:UWP)。对于 NetStandard 2.0,我可能会实际使用它们,但在尝试之前,我想知道您能否告诉我这是否可行。目前我正在使用此代码:

 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
    }

但是现在我也想支持结构,我想要的是通过 ref 传递 setter 动作中的第一个参数。据我了解这是不可能的。

因此我想生成一个 Func 返回参数传递的修改对象。在这里我完全迷路了,对我来说太难了。

即使您可能找到了解决方案,我可能仍会考虑切换 100% 操作码,因为按值返回和传递可能会影响我的应用程序的性能。

令人惊奇的是,这个带有自定义委托类型的简化传递引用 Expression 示例可以在完整框架上运行。我完全不确定它是否会在没有完整 Compile() 的平台上以相同的方式工作,因为你无法在其他任何地方真正代表 ref T比堆栈,但是......值得一试:

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);       
    }
}

请注意,struct 副本(因为缺少 ref 支持)不是世界末日,除非你有 巨大 structs.

我不明白这是怎么回事,但这似乎是对的

    public static CastedAction<T> MakeSetter(FieldInfo field)
    {
        if (field.FieldType.IsInterfaceEx() == true && field.FieldType.IsValueTypeEx() == false)
        {
            ParameterExpression targetExp = Expression.Parameter(typeof(T).MakeByRefType(), "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);

            var setter = Expression.Lambda<ActionRef<T, object>>(assignExp, targetExp, valueExp).Compile();

            return new CastedAction<T>(setter); 
        }

        throw new ArgumentException("<color=orange>Svelto.ECS</color> unsupported field (must be an interface and a class)");
    }

public delegate void ActionRef<T, O>(ref T target, O value);

public class CastedAction<T>  
{
    readonly ActionRef<T, object> setter;

    public CastedAction(Delegate setter)
    {
        this.setter = (ActionRef<T, object>)setter;
    }

    public CastedAction(ActionRef<T, object> setter)
    {
        this.setter = setter;
    }

    public void Call(ref T target, object value)
    {
        setter(ref target, value);
    }
}