委托的 out / ref 参数

out / ref parameter for delegate

代表:

return delegate( IQueryable<MySearchResultItem> query, Expression<Func<MySearchResultItem, object>> lambda, Wrapper wrapper)
{
    wrapper.query = query.OrderBy(lambda);
    query = query.OrderBy(lambda);
};

包装器class:

public class Wrapper
{
    public IQueryable<MySearchResultItem> query { get; set; }
}

当我执行这个委托时,我希望在这个函数结束后查询会被更改,但它没有。所以我假设查询是按值传递的(而不是按引用传递)

但是,当我为此查询创建包装器 class 时,将查询添加到包装器 class 并将其也一起传递。然后在完成此方法后,包装器 class 内的查询已更改(因此此包装器 class 是通过引用传递的?)

这是怎么回事?

它是通过引用传递的,但是你不是在引用上操作而是覆盖它。就像在 C 中,当您将新地址分配给指针而不是对指针的值进行操作时。它与包装器一起工作 class,因为您处理引用而不是覆盖它。

如果您还想修改引用,请使用 ref 运算符。

return delegate( ref IQueryable<MySearchResultItem> query,

编辑: 当然,这需要有匹配的代表签名,它不适用于 Func<T1,T2, TResult>

public delegate void MyDelegate(ref IQueryable<object> query, Expression<Func<object, object>> lambda);

private MyDelegate Create()
{
    return delegate(ref IQueryable<object> query, Expression<Func<object, object>> lambda)
    {
        query = query.OrderBy(lambda);
    };
}

您可能混淆了 C# "by reference" 和 C++ "by reference"。

在你的代码中,query是按引用传递的,也就是说对query的值的引用是按值传递的。因此,更改 query 将更改引用所引用的值。但是,更改引用本身没有任何作用。

query 是不可变的 - 无法更改该值。您只能创建一个新查询,该查询本身包含旧查询。这正是例如OrderBy 会 - 它不会更改 query。这是 LINQ 和 C# 中类似函数式方法的核心特性之一——可变代码通常很难以一般方式处理,因此您要避免它,尤其是在接口上。

所以你需要做的是通过引用传递引用,而不是通过值。这正是您通过提供 Wrapper class 所做的。也可以使用 ref 关键字来执行此操作,但这完全没有必要,而且在您的情况下很难处理。 ref 只对值类型有意义,尽管对于引用类型也有一些有用的情况;不过,它们非常罕见。

但最好和最简单的方法是简单地遵循简单的原则:不更改任何内容,只 return 一个包含更改的对象。使您的代表 return 成为查询,而不是修改参数:

delegate IQueryable<...> YourDelegate(IQueryable<...> query);

IQueryable<...> YourMethod(IQueryable<...> query)
{
  return query.OrderBy(...);
}