Cannot Use Ref or Out Parameter in Lambda Expressions

Cannot use ref or out parameter in lambda expressions

Lambdas have the appearance of changing the lifetime of variables that they capture. For instance, the following lambda expression causes the parameter p1 to live longer than the current method frame as its value can be accessed after the method frame is no longer on the stack

Func<int> Example(int p1) {
return () => p1;
}

Another property of captured variables is that changes to the variables are also visible outside the lambda expression. For example, the following code prints out 42

void Example2(int p1) {
Action del = () => { p1 = 42; };
del();
Console.WriteLine(p1);
}

These two properties produce a certain set of effects which fly in the face of a ref parameter in the following ways:

  • ref parameters may have a fixed lifetime. Consider passing a local variable as a ref parameter to a function.
  • Side effects in the lambda would need to be visible on the ref parameter itself. Both within the method and in the caller.

These are somewhat incompatible properties and are one of the reasons they are disallowed in lambda expressions.

Cannot use ref or out parameter inside an anonymous method

Well, you're not allowed to use a ref or a out parameter in an anonymous method or a lambda, just as the compiler error says.

Instead you have to copy the value out of the ref parameter into a local variable and use that:

var nodeId = c.NodeID;
var coordinates = from r in xdoc.Descendants("move")
where int.Parse(r.Attribute("id").Value) == nodeId
...

How to pass 'out' parameter into lambda expression

As the compiler error indicates, it isn't allowed to use out or ref parameters inside lambda expressions.

Why not just use a copy? It's not like the lambda wants to mutate the variable anyway, so I don't see a downside.

string prettyNameCopy = prettyName;
var matchingProperties = getLocalProperties()
.Where(prop => prop.Name == prettyNameCopy);

Alternatively, you can use a local throughout (to evaluate the appropriate name etc.), and assign the outparameter prettyName just before returning from the method. This will probably be more readable if there isn't significant branching within the method.

Getting Cannot use ref or out parameter inside an anonymous method, lambda expression, or query expression for an in parameter

Behind the scenes, in is a ref parameter, but with fancy semantics. The same as out is a ref parameter with fancy semantics. The compiler message could be clearer, perhaps - that might be a good bug to log on the Roslyn github. But: it is correct to error. I would agree that the error should explicitly mention in parameters.

How to pass a ref parameter inside lambda expression? - Thread issue

You can't use ref. See this very same question.

That being said, the reason you mentioned (The object is big and used often, I don't want to pass it in value type) is not valid.

You won't pass your parameter as value if you remove ref. There won't be any performance/memory gain to use ref here.
See value vs reference.

Cannot use use ref or out parameter for anonymous methods

I would recommend sharing strClientId as static variable:

static BackgroundWorker worker; 
static string strClientId;
static void Main(string[] args)
{
worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerAsync();
Console.ReadLine();
}
static void worker_DoWork(object sender,DoWorkEventArgs e)
{
strClientId = "2211";
Authenticate();
}
static void Authenticate()
{
Timer timer = new Timer(500);
timer.Elapsed += (sender, e) => Authenticates_Timer(sender, e);
timer.Start();
}
static void Authenticates_Timer(object sender, ElapsedEventArgs e)
{
strClientId = "";
}

Will it work for you?

Update: Ok, let's try this one:

static void worker_DoWork(object sender,DoWorkEventArgs e) 
{
string strClientId = "";
var setClientId = new Action<string>(v => { strClientId = v; });
setClientId("2211");
Authenticate(setClientId);
}

static void Authenticate(Action<string> setClientId)
{
Timer timer = new Timer(500);
timer.Elapsed += (sender, e) => Authenticates_Timer(sender, e, setClientId);
timer.Start();
}

static void Authenticates_Timer(object sender, ElapsedEventArgs e, Action<string> setClientId)
{
setClientId("");
}

Out variables and nested lambdas

For why you can't use ref/out parameters, see this post.

You seem to want to recreate a "loop" statement with a "break" functionality. Rather than using out parameters to implement the break statement, you can use another Action<bool> delegate instead.

public class Loop
{

protected Action<Action<bool>> action;

public Loop Infinite(Action<Action<bool>> actn)
{
action = actn;

Start();

return this;
}

protected void Start()
{
bool exit = false;

while (!exit)
{
// passes the Action<bool> that assigns the given bool to exit
action((b) => exit = b);
}
}
}
int outer = 0;

Loop l1 = new Loop().Infinite(setOuterExit =>
{
int inner = 0;
outer++;

Loop l2 = new Loop().Infinite(setInnerExit =>
{
inner++;
Console.WriteLine($"{outer}-{inner}");
setInnerExit(inner >= 3);
setOuterExit(inner >= 3);
});

setOuterExit(outer >= 3);
});

This gives a final outer value of 3. But if you wanted that, you could have just removed the setOuterExit call in the inner loop - it also gives 3 as output.

Right now the value assigned to exit by the inner setOuterExit call is being "overwritten" by the outer setOuterExit call, so the outer loop doesn't break immediately when it hits the inner setOuterExit. We could change the assignment to exit to:

action((b) => exit |= b); 

So that if exit is ever set to true, no other values can overwrite it. This will solve this problem, but note that setOuterExit still won't work the same as a real break; statement. The code immediately after setOuterExit still gets executed, for example.

Cannot setup the function with out parameter using lambda expression

This might work for you:

//define the callback delegate
delegate void TryGetCallback(CompositeKey key, out string str, Func<CompositeKey, string> func);

mock.Setup(x => x.TryGet(
It.IsAny<CompositeKey>(),
out It.Ref<string>.IsAny,
It.IsAny<Func<CompositeKey, string>>()))
.Callback(new TryGetCallback((CompositeKey key, out string str, Func<CompositeKey, string> func) =>
{
str = "foo bar";
}))
.Returns(true);

with such a setup you could archive what you want

string actualValue;
bool result = mock.Object.TryGet(new CompositeKey(), out actualValue)
//actualValue = "foo bar", result = true


Related Topics



Leave a reply



Submit