5

I want to assign to a ref variable from inside a lambda. This is a simplified MRE of what I have:

public class Foo
{
    public int Value { get; private set; }
    public Foo(int value) => this.Value = value;
}

public class Test
{
    Foo F1 = new Foo(1);
    Foo F2 = new Foo(2);

    // library method, cannot change
    void LoadAsync(int value, Action<Foo> onSuccess)
    {
        // this happens async on a future frame
        Foo f = new Foo(value);
        onSuccess(f);
    }

    // my method, can change parameters and body, cannot change return type
    void LoadAndAssignAsync(int value, ref Foo fooToAssign)
    {
        LoadAsync(value, loadedFoo => fooToAssign = loadedFoo); // this is not allowed

        // because the load happens async, I cannot assign `fooToAssign` here,
        // it needs to happen inside the lambda
    }

    void Start()
    {
        LoadAndAssignAsync(11, ref F1); // I want to keep this simple
        LoadAndAssignAsync(24, ref F2);

       // I want a simple way of saying:
       // load and when done assign the result to the variable (field) I specify
    }
}

Because of the reasons shown here a lambda cannot capture a reference variable. I am looking for a simple alternate method, if it exists.

I can change and pass a lambda instead LoadAndAssign(11, result => F1 = result, but it is cumbersome, I am hoping I can keep the simple ref F1 argument.


async here means Unity's "async", aka coroutines, more specifically Addressables' AsyncOperationHandle

bolov
  • 72,283
  • 15
  • 145
  • 224
  • 1
    It's not possible to have ref here as explained in the linked answer. I would consider using async await - wrap LoadAsync in a method which returns Task and then do F = await ThatMethod(). – Evk Nov 17 '22 at 05:34
  • It seems you should be using `AssetReferenceT`, i.e. replace `Foo F1` with `AssetReferenceT F1` and `LoadAndAssign(11, ref F1)` with `F1.LoadAssetAsync()`. This has the added benefit that it keeps the asset ID close to the eventual location where the asset is loaded, so you don't have to remember if F1 has id 11 or id 24. – user2407038 Nov 17 '22 at 20:12
  • Is `LoadAndAssign(11, _ => F1 = _)` so much more cumbersome than `LoadAndAssign(11, ref F1)`? Just 2 extra characters not counting whitespace. The parameter name has zero semantic content. Using `_` reflects that as well as being shrt – Anton Tykhyy Nov 21 '22 at 06:33

1 Answers1

-1

you can write code like :

void LoadAndAssignAsync(int value, ref Foo fooToAssign)
{
    Foo aa = null;
    LoadAsync(value, loadedFoo => aa = loadedFoo); // this is not allowed
    fooToAssign = aa;
youbl
  • 134
  • 1
  • 11
  • Is I've written in the post the load happens async and so it is wrong to assing to `fooToAssign` after the call to `LoadAsync`, it needs to be inside the lambda as that is called when the load completes. – bolov Nov 16 '22 at 11:47
  • oh, I'm wrong, async method can't asign, your method return void, why don't using return value and avoid ref? like `Foo LoadAndAssignAsync(int value)` – youbl Nov 16 '22 at 13:28
  • my real method returns an `AsyncOperationHandle` that is a chain of multiple async operations. – bolov Nov 16 '22 at 15:11
  • OK, asign it in lambda isn't work. Another way, you can new Foo before lambda, then set it's Property inside lambda, it can works. – youbl Nov 17 '22 at 01:53