委托的 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(...);
}
代表:
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(...);
}