C# 如何从嵌套深度级别的表达式中设置值?

C# 如何从嵌套深度级别的表达式中设置值?,c#,.net-4.0,C#,.net 4.0,我的问题与以下两个问题非常相似,但我还有一个附加要求,即这些问题不能满足 就像这些问题一样,我有一个表达式,我想在其中为指定的属性设置一个值。如果表达式的主体只有一层深度,比如x=>x.FirstName,那么这些解决方案非常有效,但是如果主体更深,比如x=>x.Parent.FirstName,这些解决方案根本不起作用 是否有某种方法可以采用这个更深层次的表达式并为其设置值?我不需要一个非常健壮的执行延迟解决方案,但我确实需要一些可以在对象上执行的东西,无论是一个层次还是多个层次的深度

我的问题与以下两个问题非常相似,但我还有一个附加要求,即这些问题不能满足

就像这些问题一样,我有一个
表达式
,我想在其中为指定的属性设置一个值。如果表达式的主体只有一层深度,比如
x=>x.FirstName
,那么这些解决方案非常有效,但是如果主体更深,比如
x=>x.Parent.FirstName
,这些解决方案根本不起作用

是否有某种方法可以采用这个更深层次的表达式并为其设置值?我不需要一个非常健壮的执行延迟解决方案,但我确实需要一些可以在对象上执行的东西,无论是一个层次还是多个层次的深度。我还需要支持数据库中最典型的类型(
long
int?
string
Decimal
DateTime?
,等等,尽管我不关心更复杂的事情,比如地理类型)

为了便于交谈,假设我们正在处理这些对象,但假设我们需要处理N个级别的深度,而不仅仅是1或2:

public class Parent
{
    public string FirstName { get; set; }
}

public class Child
{
    public Child()
    {
        Mom = new Parent(); // so we don't have to worry about nulls
    }

    public string FavoriteToy { get; set; }
    public Parent Mom { get; set; }
}
假设这是我们的单元测试:

[TestFixture]
public class Tests
{
    [Test]
    public void MyTest()
    {
        var kid = new Child();
        Expression<Func<Child, string>> momNameSelector = (ch => ch.Mom.FirstName);
        Expression<Func<Child, string>> toyNameSelector = (ch => ch.FavoriteToy);

        kid.ExecuteMagicSetter(momNameSelector, "Jane");
        kid.ExecuteMagicSetter(toyNameSelector, "Bopp-It!");

        Assert.That(kid.Mom.FirstName, Is.EqualTo("Jane"));
        Assert.That(kid.FavoriteToy, Is.EqualTo("Bopp-It!"));
    }
}
[TestFixture]
公开课考试
{
[测试]
公共无效MyTest()
{
var kid=new Child();
表达式momNameSelector=(ch=>ch.Mom.FirstName);
表达式toyNameSelector=(ch=>ch.FavoriteToy);
kid.ExecuteMagicSetter(妈妈的名字,选“Jane”);
kid.ExecuteMagicSetter(toyNameSelector,“Bopp It!”);
Assert.That(kid.Mom.FirstName,是.EqualTo(“Jane”);
Assert.That(kid.FavoriteToy,Is.EqualTo(“Bopp It!”);
}
}
我们正在研究的扩展方法(我不认为它需要一个扩展方法,但它似乎足够简单)如下所示:

public static TEntity ExecuteMagicSetter<TEntity, TProperty>(this TEntity obj, Expression<Func<TEntity, TProperty>> selector, TProperty value)
    where TEntity : class, new() // I don't require this but I can allow this restriction if it helps
{
    // magic
}
publicstatictenty ExecuteMagicSetter(此tenty对象、表达式选择器、TProperty值)
where tenty:class,new()//我不需要这个,但是如果有帮助的话,我可以允许这个限制
{
//魔力
}

另外,这个版本的代码是在SO编辑器中编写的-我为愚蠢的语法问题道歉,但这应该是非常接近的#正如我在评论中所说的,它不应该那么复杂。使用选择器,只需向表达式添加赋值。您只需要编译并运行表达式

public static TEntity ExecuteMagicSetter<TEntity, TProperty>(
        this TEntity obj,
        Expression<Func<TEntity, TProperty>> selector,
        TProperty value)
{
    var setterExpr = CreateSetter(selector);
    setterExpr.Compile()(obj, value);
    return obj;
}

private static Expression<Action<TEntity, TProperty>> CreateSetter<TEntity, TProperty>
        (Expression<Func<TEntity, TProperty>> selector)
{
    var valueParam = Expression.Parameter(typeof(TProperty));
    var body = Expression.Assign(selector.Body, valueParam);
    return Expression.Lambda<Action<TEntity, TProperty>>(body,
        selector.Parameters.Single(),
        valueParam);
}
公共静态TEntity执行器MAGICSetter(
这是一个潜在的目标,
表达式选择器,
t属性值)
{
var setterExpr=CreateSetter(选择器);
seterexpr.Compile()(对象,值);
返回obj;
}
私有静态表达式CreateSetter
(表达式选择器)
{
var valueParam=表达式.参数(typeof(TProperty));
var body=Expression.Assign(selector.body,valueParam);
返回表达式.Lambda(body,
selector.Parameters.Single(),
valueParam);
}

成员表达式的嵌套深度应该无关紧要,只要表达式最终位于属性/字段即可。然后用这个表达式,你可以添加一个赋值,你就完成了。@JeffMercado我尝试的这些问题的每一个解决方案最终都会遇到这样的问题:使用反射调用
父对象的setter方法(在我上面的例子中),但传递了
子对象的实例,因此,在运行时存在一个类型不匹配,导致失败。我觉得缺少的神奇之处是递归或迭代地获取子属性对象,然后将它们传递到setter方法中。那么就包括您对这个问题的尝试。您描述的问题听起来与嵌套表达式无关。如果您不是手动生成这些选择器表达式并让编译器处理,那么它首先必须是一个有效的表达式。我尝试的解决方案是在我列出的问题中发布的,这些问题与我的问题非常相似。如果你愿意,我可以在这里复制,但我觉得这是多余的。这就是您想要的吗?重复使用lambda表达式的新功能。你能帮我使用你写的方法吗。