2

How can I pass a ref to a lambda ? I've seend posts suggesting a delegate but I can't get it to work ..

I tried:

public class State<T>
{
  public State() { }
  public State(T t) { this.t = t; }
  readonly object theLock = new object();
  T t;

  public void Lock(Action<ref T> action) { lock (theLock) action(ref t); }
}

however this does not compile, I get 'unexpected token ref'

I then tried:

public class State<T>
{
  public State() { }
  public State(T t) { this.t = t; }
  readonly object theLock = new object();
  T t;

  public delegate void ActionRef<X>(ref X t);
  public void Lock(ActionRef<T> action) { lock (theLock) action(ref t); }
}

Which compiles but I'm not able to use it

If I try:

  var v = new State<int>(5);
  v.Lock(t => t + 1);

I get Parameter 1 must be declared with the 'ref' keyword

If I try:

  var v = new State<int>(5);
  v.Lock(ref t => t + 1);

I get A ref or out value must be an assignable variable

How can I get this to work ? that is pass an Action to Lock which locks the lock and then calls the lambda with a ref ?

(tested in VS19 community preview .NET Core console app)

(this is different than Cannot use ref or out parameter in lambda expressions as there it's about a lambda using a ref in it's closure)

jazb
  • 5,498
  • 6
  • 37
  • 44
kofifus
  • 17,260
  • 17
  • 99
  • 173
  • gsharp that question is about a labda using a ref in its closure – kofifus Dec 22 '18 at 06:56
  • Even that the use is a bit different, the second answer in the marked as duplicate seems to explain it – Slai Dec 22 '18 at 08:57
  • 1
    People confuse two different things. _(1)_ Declaring a delegate type which has `ref` or `out` on some of its parameters, and using that delegate type with an anonymous function (such as a lambda). This is easy enough (and the asker has an answer below). _(2)_ Attempting to "capture" (or close over) a `ref` or `out` parameter from the containing method, inside an anonymous function. This is what the "duplicate" question is about; he tries `a => a == value` of type `Func`, but it is "physically" impossible because `value` there is an `out` parameter of the containing named method. – Jeppe Stig Nielsen Dec 22 '18 at 11:31
  • @kofifus sorry my bad I've marked it for reopen. – gsharp Dec 24 '18 at 07:30

1 Answers1

2

(credit goes to PetSerAI ..)

I was very close, State<T> is the same:

public class State<T>
{
  public State() { }
  public State(T t) { this.t = t; }
  readonly object theLock = new object();
  T t;

  public delegate void ActionRef(ref T t);
  public void Lock(ActionRef action) { lock (theLock) action(ref t); }
}

but using it is like this:

var s = new State<int>(5);
s.Lock((ref int v) => v=v+3);

For completion here is ReadOnlyState<T>:

public class ReadOnlyState<T>
{
  public ReadOnlyState(T t) { this.t = t; }

  readonly object theLock = new object();
  readonly T t;
  public delegate void ActionRef(in T t);
  public void Lock(ActionRef action) { lock (theLock) action(in t); }
}

var ros = new ReadOnlyState<int>(5);
ros.Lock((in int v) => {
   v.f();
   v=2; // does not compile
});
kofifus
  • 17,260
  • 17
  • 99
  • 173
  • 1
    Correct. Note that the `public delegate void ActionRef(ref X t);` is nested inside the `class` in your example. You can also move it out and have it as a direct member of the namespace; because it does not use the `T` of the containing `State`, so you do not have to nest it. – Jeppe Stig Nielsen Dec 22 '18 at 11:35
  • 1
    The other option is to keep `ActionRef` nested inside the generic class, and then make `ActionRef` itself non-generic, and let it access the `T` from `State`. Like this: `public class State { /* … */ public delegate void ActionRef(ref T t); /* … */ }` – Jeppe Stig Nielsen Dec 22 '18 at 11:38
  • I really like your second suggestion, amended the answer, thx! – kofifus Dec 22 '18 at 11:47