【问题标题】:C# lambda ref outC# lambda 引用输出
【发布时间】:2010-12-20 15:24:58
【问题描述】:

我正在尝试这样做,但它不起作用。有什么建议吗?

int test_i = 0;
DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(test_i);
test_i <- still is 0 and not 3!!!

public void DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(int i)
{
    DisableUi();
    m_commandExecutor.ExecuteWithContinuation(
                () =>
                    {
                        // this is the long-running bit
                        ConnectToServer();
                        i = 3; <-------------------------- 
                        // This is the continuation that will be run
                        // on the UI thread
                        return () =>
                                    {
                                        EnableUi();
                                    };
                    });
}

为什么我不能将 test_i 设置为 3?我也试过 ref 和 out,但它不起作用。

我能做些什么来解决它?

编辑

我已经试过了,但是这个方法之外的 dataSet 仍然是空的。

public static void Select(DataGridView dataGridView, ref DataSet dataSet, params object[] parameters)
  {
     var _dataSet = dataSet;
     AsyncCommandExecutor commandExecutor = new AsyncCommandExecutor(System.Threading.SynchronizationContext.Current);
     commandExecutor.ExecuteWithContinuation(
     () =>
     {
        // this is the long-running bit
        _dataSet = getDataFromDb(parameters);

        // This is the continuation that will be run on the UI thread
        return () =>
        {
           dataGridView.DataSource = _dataSet.Tables[0].DefaultView;
        };
     });
     dataSet = _dataSet;
  }

【问题讨论】:

  • 我更新了我的答案。希望它有所帮助:o)

标签: c# .net .net-3.5 lambda


【解决方案1】:

当使用 ref 关键字传递变量时,您不能在 lambda 表达式中使用它。尝试在 lambda 内部使用局部变量,并在其外部分配 ref 变量,如果可能的话(有些简化的示例):

private static void Main(string[] args)
{
    int i = 0;
    DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(ref i);
    Console.WriteLine(i);
}


public static void DoSomethingThatTakesAgesAndNeedsToUpdateUiWhenFinished(ref int i)
{
    int temp = i;
    Thread t = new Thread(() =>
    {
        temp = 3; // assign the captured, local variable    
    });
    t.Start();
    t.Join();

    i = temp; // assign the ref parameter
}

更新
针对更新的答案:您的问题是 lambda 表达式内的 _dataSet 与 lambda 表达式外的 dataSet 变量不同。您可以执行以下操作:

class DataSetContainer
{
    public DataSet DataSet { get; set; }
}

现在我们有了一个带有属性的引用类型,我们可以在 lambda 表达式中安全地修改它:

public static void Select(DataGridView dataGridView,
                          DataSetContainer dataSetContainer, 
                          params object[] parameters)
{
    AsyncCommandExecutor commandExecutor = new AsyncCommandExecutor(System.Threading.SynchronizationContext.Current);
    commandExecutor.ExecuteWithContinuation(
    () =>
    {
        // this is the long-running bit
        dataSetContainer.DataSet = getDataFromDb(parameters);

        // This is the continuation that will be run on the UI thread
       return () =>
       {
           dataGridView.DataSource = _dataSet.Tables[0].DefaultView;
       };
    });

}

}

在上面的代码中,lambda 表达式将更新传递给Select 方法的DataSetContainer 实例的DataSet 属性。由于您没有修改传递的参数本身,而只是修改了该实例的成员,因此不需要 ref 关键字,我们也解决了闭包问题。

更新 2
现在,当我打开我的大脑时,我意识到Select 方法进行了异步调用。很可能因为代码看起来最后一行是Select 方法将在分配_dataSet 之前很久执行,因此它将是null。要解决这个问题,您可能需要考虑使用某种信号机制(例如 ManualResetEventAutoResetEvent)来了解分配何时完成。

【讨论】:

  • 是否可以像在我的示例中一样使用它,但使用 dataSet 而不是 int i?
  • @Jooj:如果你传递一个引用类型,你应该能够在 lambda 表达式中修改它的成员属性/字段。
【解决方案2】:

lambda 表达式中的i 变量指的是方法的参数i。作为一种解决方法,您可以使其引用全局变量(肮脏的解决方案)。

顺便说一句,you can't capture ref and out variables in lambdas,但您可以将它们作为参数。您需要更改委托的签名和接收委托的方法的实现,这可能不合适:

(out int i) => { i = 10; }

【讨论】:

    猜你喜欢
    • 2010-11-07
    • 1970-01-01
    • 1970-01-01
    • 2011-03-31
    • 2020-11-09
    • 2014-02-21
    • 1970-01-01
    • 1970-01-01
    • 2021-12-06
    相关资源
    最近更新 更多