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;
}
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.
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; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With