简体   繁体   中英

out / ref parameter for delegate

Delegate:

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

Wrapper class:

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

When I execute this delegate I expected for the query to be changed after this function is over, but it didn't. So i assume that the query passed by value ( instead of by reference )

But when I create a wrapper class for this query, add the query to the wrapper class and pass this along as well. Then after this method is done the query inside the wrapper class has been changed ( so this wrapper class was passed by reference ? )

What's going on here ?

It is passed by reference, but you are not operating on the reference but rather overwrite it. It is like in C when you assign a new address to a pointer instead of operating on the value of the pointer. It works with the wrapper class, because you work on the reference instead of overwriting it.

If you want to modify the reference as well use the ref operator.

return delegate( ref IQueryable<MySearchResultItem> query,

Edit: Of course that requires to have matching delegate signature and it won't work with 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);
    };
}

You may be mixing up C# "by reference" with C++ "by reference".

In your code, query is passed by reference, which means that the reference to the value of query is passed by value. Thus, changing query will change the value the reference is referencing. However, changing the reference itself does nothing.

query is immutable - there's no way to change the value. You can only create a new query, that contains the old query within itself. And that's exactly what eg OrderBy does - it doesn't change the query . This is one of the core features of LINQ and similar functional approaches in C# - mutable code is generally much harder to deal with in a general way, so you want to avoid it, especially on interfaces.

So what you need to do is pass the reference by reference, not by value. That's exactly what you've done by providing the Wrapper class. It's also possible to use ref keyword to do this, but it's completely unnecessary and rather hard to deal with in your case. ref only really makes sense with value types, though there are useful cases even for reference types; they are quite rare, though.

But the best and easiest approach is to simply follow the simple principle: don't change anything, just return an object that contains the change. Make your delegate return the query, rather than modifying the argument:

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM