委托的out/ref参数

本文关键字:ref 参数 out | 更新日期: 2024-09-12 14:13:30

委托:

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

包装类别:

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

当我执行这个委托时,我希望在这个函数结束后更改查询,但没有。因此,我假设查询是通过值(而不是通过引用)传递的

但是,当我为这个查询创建一个包装类时,将查询添加到包装类中,并将其传递出去。然后,在这个方法完成后,包装类内部的查询已经更改(所以这个包装类是通过引用传递的?)

这是怎么回事?

委托的out/ref参数

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

在代码中,query是通过引用传递的,这意味着对query值的引用是通过值传递的。因此,更改query将更改引用所引用的值。但是,更改引用本身没有任何作用。

query是不可变的——没有办法更改值。您只能创建一个新查询,该查询本身包含旧查询。这正是OrderBy的作用——它不会改变query。这是LINQ的核心功能之一,也是C#中类似的功能方法——可变代码通常很难以通用的方式处理,所以您希望避免它,尤其是在接口上。

因此,您需要做的是通过引用传递引用,而不是通过值传递引用。这正是您通过提供Wrapper类所做的。使用ref关键字也可以做到这一点,但这是完全没有必要的,而且在您的情况下很难处理。ref只对值类型有意义,尽管即使对于引用类型也有有用的情况;不过,它们相当罕见。

但最好、最简单的方法是遵循简单的原则:不更改任何内容,只返回包含更改的对象。让您的委托返回查询,而不是修改参数:

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

它是通过引用传递的,但你不是在引用上操作,而是覆盖它。这就像在C中,你给指针分配一个新地址,而不是在指针的值上操作。它与包装类一起工作,因为你处理引用而不是覆盖它

如果要修改引用,也可以使用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);
    };
}