Andrei Zhukouski
Andrei Zhukouski

Reputation: 3506

Ref in async Task

How I can to pass a reference as a parameter to Async method in Windows Store App ?
I'm looking for something like this:

var a = DoThis(ref obj.value);

public async Task DoThis(ref int value)
{
    value = 10;
}

But error:

Async methods cannot have ref or out parameters

Has any another way?

Note:
I need to pass exactly obj.value. This method would be used by different types of objects, by same type of objects, by one object, but I will pass obj.val_1, obj.val_2 or obj.val_10. All values will be same type (for ex string)

Upvotes: 8

Views: 17261

Answers (4)

Nuffin
Nuffin

Reputation: 3972

If you don't care about a little overhead and possibly prolonged lifetime of your objects, you could emulate the ref behavior by passing a setter and a getter method to the function, like this:

public async Task DoStuff(Func<int> getter, Action<int> setter)
{
    var value1 = getter();
    await DoSomeOtherAsyncStuff();
    setter(value1 * value1);
}

And call it like this:

await DoStuff(() => obj.Value, x => obj.Value = x);

Upvotes: 21

Grx70
Grx70

Reputation: 10349

You can always use the Task<> class and return the desired value. Then Your code would look something like:

var a = DoThis(obj.value);
obj.value = a.Result;

public async Task<int> DoThis(int value)
{
    int result = value + 10; //compute the resulting value
    return result;
}

EDIT

Ok, the other way to go with this that I can think of is encapsulating the update of the given object's member in a method and then passing an action invoking this method as the task's argument, like so:

var a = DoThis(() => ChangeValue(ref obj.value));

public void ChangeValue(ref int val)
{
    val = 10;
}

public async Task DoThis(Action act)
{
    var t = new Task(act);
    t.Start();
    await t;
}

As far as I tested it the change was made in the child thread, but still it's effect was visible in the parent thread. Hope this helps You.

Upvotes: 4

Dave
Dave

Reputation: 8471

You can't do this as you have it (as you know). So, a few work arounds:

You can do this by passing the initial object since it will be a reference type

var a = DoThis(obj);  

public async Task DoThis(object obj) //replace the object with the actual object type
{
    obj.value = 10;
}

EDIT

Based upon your comments, create an interface and have your classes implement it (providing it's always the same type you want to pass). Then you can pass the interface which is shared (maybe over kill, depends on your needs, or even unrealistic amount of work).

Or, provide a base class with the property! (I don't like this suggestion but since you're asking for something which can't be done it may suffice although I don't recommend it).

An example of the interface is here (not using what you have, but close enough using a Colsone App)

using System;

namespace InterfacesReferenceTypes
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            DoThis(mc);
            Console.WriteLine(mc.Number);
            Console.ReadKey();
        }

        static void DoThis(IDemo id)
        {
            id.Number = 10;
        }
     }

    class MyClass : IDemo
    {
        //other props and methods etc
        public int Number { get; set; }
    }

    interface IDemo
    {
        int Number { get; set; }
    }
}

EDIT2

After next comments, you will have to still use an interface, but re assign the value afterwards. I'm sure there is a better way to do this, but this works:

using System.Text;

namespace InterfacesRerefenceTypes
{
    class Program
    {
        static void Main(string[] args)
        {
            MyClass mc = new MyClass();
            Console.WriteLine(mc.Number);
            mc.val1 = 3;
            mc.val2 = 5;
            mc.Number = mc.val2;
            DoThis(mc);
            mc.val2 = mc.Number;

            Console.WriteLine(mc.val2);
            Console.ReadKey();
        }

        static void DoThis(IDemo id)
        {
            id.Number = 15;
        }
    }

    class MyClass : IDemo
    {
        public int val1 { get; set; }
        public int val2 { get; set; }
        public int Number { get; set; }
    }

    interface IDemo
    {
        int Number { get; set; }
    }
}

Upvotes: 3

Darin Dimitrov
Darin Dimitrov

Reputation: 1039438

You could directly pass the object itself and set the value of the corresponding property inside the method:

var a = DoThis(obj);

public async Task DoThis(SomeObject o)
{
    o.value = 10;
}

And if you do not have such object simply write one and have the async method take that object as parameter:

public class SomeObject
{
    public int Value { get; set; }
}

Upvotes: 13

Related Questions