【问题标题】:Ref in async Task异步任务中的参考
【发布时间】:2018-08-30 17:15:49
【问题描述】:

如何将引用作为参数传递给 Windows Store App 中的 Async 方法?
我正在寻找这样的东西:

var a = DoThis(ref obj.value);

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

但是错误:

异步方法不能有 ref 或 out 参数

还有其他方法吗?

注意:
我需要准确地传递 obj.value。此方法将被不同类型的对象、相同类型的对象、一个对象使用,但我将传递 obj.val_1、obj.val_2 或 obj.val_10。所有值都是相同的类型(例如字符串)

【问题讨论】:

    标签: c# asynchronous async-await windows-store-apps


    【解决方案1】:

    如果您不关心对象的一点开销和可能延长的生命周期,您可以通过将 setter 和 getter 方法传递给函数来模拟 ref 行为,如下所示:

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

    然后这样称呼它:

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

    【讨论】:

    • 我相信这是一个很好的答案,原因有很多,他可以通过将其通用化来扩展他许多不同类型对象的可重用性:DoStuff&lt;T&gt;(Func&lt;T&gt; getter, Action&lt;T&gt; setter)
    • Nate Diamond 我认为 Func 和 Action 已经是 Generic type Delegates
    【解决方案2】:

    您可以直接传递对象本身并在方法内部设置相应属性的值:

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

    如果您没有这样的对象,只需编写一个并让异步方法将该对象作为参数:

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

    【讨论】:

    • 请阅读问题:我需要准确传递 obj.value。此方法将被不同类型的对象、相同类型的对象、一个对象使用,但我将传递 obj.val_1、obj.val_2 或 obj.val_10
    • 请阅读您收到的错误消息:这在 C# 中不受支持,除非您自己编写一个支持将 ref/out 变量传递给 async 方法的编译器解决你的问题。你还希望我告诉你什么?向您展示如何执行不受支持的操作的工作示例?
    • 我的问题是:“还有其他方法吗?”答案必须简短:“不,因为......”或“是的,你需要做......”并且你已经提供的答案没有帮助。
    【解决方案3】:

    您始终可以使用Task&lt;&gt; 类并返回所需的值。然后您的代码将类似于:

    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;
    }
    

    编辑

    好的,我能想到的另一种方法是将给定对象成员的更新封装在一个方法中,然后传递一个调用该方法的动作作为任务的参数,如下所示:

    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;
    }
    

    据我测试,更改是在子线程中进行的,但它的效果在父线程中仍然可见。希望这对你有帮助。

    【讨论】:

    • 我知道这种方式,但我正在寻找可以在异步任务中更改值的方法。但是为你 +1。
    • 关于您的编辑:您不能在 lambda 表达式中使用 ref 参数。
    【解决方案4】:

    你不能这样做(如你所知)。因此,一些解决方法:

    您可以通过传递初始对象来做到这一点,因为它将是一个引用类型

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

    编辑

    根据您的 cmets,创建一个接口并让您的类实现它(假设它始终是您想要传递的相同类型)。然后您可以传递共享的接口(可能过度杀死,取决于您的需求,甚至是不切实际的工作量)。

    或者,提供具有该属性的基类! (我不喜欢这个建议,但既然你要求的是一些无法完成的事情,尽管我不推荐它,但它可能就足够了。

    这里有一个界面示例(不使用您拥有的,但使用 Colsone 应用程序足够接近)

    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

    在下一个 cmets 之后,您仍必须使用接口,但之后重新分配值。我确信有更好的方法可以做到这一点,但这很有效:

    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; }
        }
    }
    

    【讨论】:

    • 请阅读问题:我需要准确传递 obj.value。此方法将被不同类型的对象、相同类型的对象、一个对象使用,但我将传递 obj.val_1、obj.val_2 或 obj.val_10
    • @jimpanzer,我做到了-您的帖子从未提及它将被不同类型的对象使用!我可以假设 val_1、val_2 等都是同一类型吗?
    • val_1, val_2, val_10 - 都是相同的类型(字符串或 int 或其他,但相同)。我只需要传递值,因为我从不同的地方得到这个值。
    • @jimpanzer,已更新 - 您或许可以为此使用接口
    • 我理解你的想法,但在 MyClass 中可以是 Number、Number1、Nuber2... 和许多其他数字。我只想传递价值。但是,谢谢,您花时间。
    猜你喜欢
    • 2014-09-12
    • 1970-01-01
    • 2014-03-19
    • 2017-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-20
    • 1970-01-01
    相关资源
    最近更新 更多