Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# lambda ref out

I'm trying to do this, but it doesn't work. Some suggestions?

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

Why I can't set test_i to 3? I also tried ref and out, but it doesn't work.

What can I do to fix it?

EDIT

I've tried this, but ouside of this method dataSet still is empty.

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;
  }
like image 696
Jooj Avatar asked Dec 06 '22 04:12

Jooj


2 Answers

When passing the variable using the ref keyword, you can't use it inside a lambda expression. Try using a local variable inside the lambda and assign the ref variable outside it, if possible (somewhat simplified example):

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
}

Update
In response to the updated answer: your problem is that _dataSet inside the lambda expression is not the same variable as dataSet outside the lambda expression. What you could do is the following:

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

Now we have a reference type with a property that we can safely modify inside the lambda expression:

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

}

}

In the above code, the lambda expression will update the DataSet property of the DataSetContainer instance that is passed to the Select method. Since you are not modifying the passed argument itself but only a member of that instance there is no need for the ref keyword, and we also get around the closure issue.

Update 2
And now when I switched on my brain, I realize that the Select method makes an asynchronous call. It is quite likely as the code looks that the last line is the Select method will be executed long before _dataSet is being assigned, and as a result it will be null. To get around this you probably want to look into using some sort of signaling mechanism (such as ManualResetEvent or AutoResetEvent) to know when the assignment is done.

like image 99
Fredrik Mörk Avatar answered Dec 29 '22 08:12

Fredrik Mörk


The i variable in the lambda expression refers to the parameter i of the method. As a workaround, you can make it to refer to a global variable (dirty solution).

By the way, you can't capture ref and out variables in lambdas, but you can have them as parameters. You need to change the signature of your delegate and the implementation of the method receiving the delegate, which might not be suitable:

(out int i) => { i = 10; }
like image 38
mmx Avatar answered Dec 29 '22 07:12

mmx