I'm fairly new to C# and trying to figure out how to invoke a function from a list. I thought that List would do the job I need it to. I can get my functions into the list, but I can't seem to actually invoke them.
First I tried this:
List<Action> randEvent = new List<Action>();
void Test()
{
randEvent.Add(Test2);
Invoke(randEvent(0), 0f);
}
void Test2()
{
print("This is a test of the random event system");
}
Then this
List<Action> randEvent = new List<Action>();
void Test()
{
randEvent.Add(Test2);
randEvent(0);
}
void Test2()
{
print("This is a test of the random event system");
}
But neither works. What am I doing wrong? Is this even possible? The reason I want to do it this way is basically that I have 100 functions that I want my program to chose through at random when I call another function.
Any solution appreciated, though keep in mind I'm very new to C# and code in general still. Thanks in advance.
In C#/.NET, different method signatures have different delegate types that represent them. Action represents functions that take no arguments and return no value, like void Foo(). If the functions you want to represent take a float parameter and return nothing, you would need to use Action<float>. Functions with return values are represented with the Func family of types (Func<T>, Func<T1, T2>...).
You can only put one kind of delegate in a List<T>, so you can't mix Actions and Action<float>s.
To get an item out of a list in C#, use [n]. Like
List<Action> actions = new List<Action>();
actions.Add(Foo);
Action a = actions[0];
To invoke a delegate instance in C#, call the Invoke method on it, or just use () which is shorthand for calling Invoke. For Action, Invoke takes 0 parameters, for Action<T> it takes a single T parameter, etc.
So for your whole example:
List<Action> actions = new List<Action>();
void Test()
{
actions.Add(PrintStuff);
actions[0]();
//or
actions[0].Invoke();
//or
foreach (var a in actions) a();
}
void PrintStuff()
{
print("This is a test of the random event system");
}
You could declare sort of a list of Action and each object will point to a specific method. Example;
IList<Action> actionList = new List<Action>();
actionList.Add(() => Test2());
Then you can loop through the list and Invoke each method.
foreach(var method in actionList)
{
method.Invoke();
}
I'm assuming randEvent is a List<EventHandler> or List<Action<Int32>>.
Use Delegate.Invoke( args ) (as EventHandler and Action<T> are both Delegate sub-types).
randEvent[0].Invoke( 123 );
In C#, you can use () on a Delegate directly too:
randEvent[0]( 123 );
Related
Say you have a button on your form. You attached an anonymous function to button's Click event:
void Test()
{
int x = 10;
btn.Click += (sender, e) => { MessageBox.Show(x.ToString()); };
}
This works as expected and displays 10; means it can access local variables. My question is how and why? How does an anonymous function get access to the local context?
The actual problem I'm dealing with is that I need to upgrade (so to speak) this anonymous function to a regular function (event handler). But doing that means I'll lose access to the variable x. I can't pass it in as a parameter too because then I'll not be able to attach it to Click event (signature mismatch). I can work around this by creating global variables and all that, but how do anonymous functions make it possible to access things that were outside their scope?
Half of the point of anonymous functions are that they can capture the context in which they're specified. It's extremely convenient to be able to do so - that's the "why" part.
The way the compiler does this is to create a new class in cases where it needs to. So your code would be converted to something like:
void Test()
{
TestHelper helper = new TestHelper();
helper.x = 10;
btn.Click += helper.Method;
}
private class TestHelper
{
public int x = 10;
public void Method(object sender, EventArgs e)
{
MessageBox.Show(x.ToString());
}
}
Every use of x within Test is converted into a use of helper.x for the appropriate instance. This is how variables with different lifetimes is covered too. For example, suppose you had a loop like this:
for (int i = 0; i < 10; i++)
{
int x = i;
// Use with some anonymous function
}
then it would create a new instance of TestHelper for each iteration of the loop... whereas if x had been declared outside the loop, there'd just be a single instance that all the anonymous functions would effectively share.
When it's just this that's captured, the compiler creates an instance method within the existing class instead of creating a helper class. When there are different scopes with potentially multiple anonymous functions which capture a variety of variables, things can get a lot more complicated, with some helper classes having references to instances of other helper classes, etc.
i´m a beginner in C#,
a start a new Thread
pollprintthread = new System.Threading.Thread(PollPrint);
pollprintthread.Start();
in this Thread i call a function with a Datagridview
void PollPrint()
{
(some code)
printausfueren();
(some code)
}
public void printausfueren()
{
(some Code)
object[] row = { sqlReader[0], sqlReader[1], sqlReader[3], sqlReader[2], sqlReader[4], sqlReader[5], sqlReader[6], sqlReader[7] };
dataGridViewPrint.Rows.Add(row);
(some Code)
}
but i can´t use printausfuheren().invoke, i found any tutorials but only for Static Methods
Please Help :)
You can find a good explanation here: https://stackoverflow.com/a/12179408/67038
However, I would recommend that you wrap your call to dataGridViewPrint.Rows.Add in a method and call that method instead.
You will want to do this because there may be other actions that you wish to take on the UI when you add those rows.
For example, if you are adding a lot of rows, you are going to want to call BeginEdit/EndEdit on the grid, so that it does not try to repaint itself for every row that you add:
public void printausfueren()
{
//(some Code)
object[] rows = { sqlReader[0], sqlReader[1], sqlReader[3], sqlReader[2], sqlReader[4], sqlReader[5], sqlReader[6], sqlReader[7] };
dataGridViewPrint.Invoke(new Action(() => LoadGrid(rows)));
//(some Code)
}
private void LoadGrid(object[] rows)
{
// only need BeginEdit/EndEdit if you are adding many rows at a time
dataGridViewPrint.BeginEdit(false);
dataGridViewPrint.Rows.Add(rows);
dataGridViewPrint.EndEdit();
}
You can easily use anonymous methods to do complex invokes, such as:
yourForm.Invoke( (MethodInvoker) delegate() { dataGridViewPrint.Rows.Add(row); } );
This will auto-magically capture the row instance etc.
So, the documentation that I've found online so far regarding the Invoke property doesn't seem to be particularly good, which is actually kind of annoying, believe it or not. I understand what Invoke does - you can't safely access the GUI directly, so an invoke does it in a safe way. That's fine, but I don't understand the variables that go into the method. If I wanted to, for instance, remove text from a listbox, how would I do that? I get about this far before I get a bit lost.
private void DoStuff(string TextIWouldBeRemoving)
{
if (listboxname.InvokeRequired)
{
listboxname.Invoke(SomeMysteriousParamaters, new object[] { TextIWouldBeRemoving )};
}
}
The first parameter is the method you want to safely invoke, the second parameter is an object array of the arguments to that method
So you would write:
private void DoStuff(string TextIWouldBeRemoving)
{
if (listboxname.InvokeRequired)
{
listboxname.Invoke(DoStuff, new object[] { TextIWouldBeRemoving )};
}
else
{
// Actually remove the text here!
}
}
Invoke is all about threading.
You need to do an invoke whenever you have created a separate thread in your code, and you need to update the User Interface elements from withing the code, that is executing in that newly create thread.
You can use a BeginInvoke, instead of a synchronous Invoke method. This article has a good example:
http://msdn.microsoft.com/en-us/library/0b1bf3y3.aspx
private void button1_Click(object sender, EventArgs e)
{
if (listBox1.InvokeRequired)
{
Action<string> d = DoAnything;
listBox1.Invoke(d, new object[] { "Item 1" });
}
else
DoAnything("Item 1");
}
void DoAnything(string itemText)
{
listBox1.Items.Remove(itemText);
}
m having a winForm and in that m using BackGroundWorker control for keeping Form GUI active.
Now i'm accessing datagridview from backgroundworker_doWork() method, so i created a delegate method below:
delegate void updateGridDelegate();
private void invokeGridControls()
{
if (autoGridView.InvokeRequired)
{
updateGridDelegate delegateControl = new updateGridDelegate(invokeGridControls);
autoGridView.Invoke(delegateControl);//here i need to do something to access autoGridView.Rows.Count
}
}
and in the backgroundworker_DoWork() event m accessing datagridview as
int temp2noofrows = autoGridView.Rows.Count - 1;// here i dn't understand how to call my delegate method, so i can avoid cross threading access error
Try with Action Delegate
and assuming that you are using .net 2.0 and above
autoGridView.Invoke(
new Action(
delegate()
{
int temp2noofrows = autoGridView.Rows.Count - 1;//
}
)
);
The problem you are going to have with things like this is you will need a very specific update method for the delegate to run. For example updating the text in a textbox.
Create a delegate that has the same signature as a method that has been previously defined:
public delegate void UpdateTextCallback(string text);
In your thread, you can call the Invoke method on the TextBox, passing the delegate to call, as well as the parameters.
myTextBox.Invoke(new UpdateTextCallback(this.UpdateText),
new object[]{"Text generated on non-UI thread."});
And this is the actual method that will run your code.
// Updates the textbox text.
private void UpdateText(string text)
{
// Set the textbox text.
myTextBox.Text = text;
}
Note: Do not create a method that matches the EventHandler delegate signature and pass that. The implementation of Invoke on the Control class will not take into account the parameters passed to Invoke if the type of the delegate is EventHandler. It will pass the control that Invoke was called on for the sender parameter as well as the value returned by EventArgs.Empty for the e parameter.
So in your case you need to make sure you pass in all the information you need in order to update your grid.
Consider
Action _captureAction;
private void TestSimpleCapturedAction()
{
Action action = new Action(delegate { });
Action printAction = () => Console.WriteLine("Printing...");
action += printAction;
CaptureActionFromParam(action);
action -= printAction;
_captureAction(); //printAction will be called!
}
private void CaptureActionFromParam(Action action)
{
_captureAction = () => action();
}
The reason printAction will be called by _captureAction is that the line
action -= printAction;
Actually translates into
action = (Action) Delegate.Remove(action, printAction);
so the action captured by _captureAction in CaptureActionFromParam() is not changed - only the local 'action' variable in TestSimpleCapturedAction() is affected.
My desired behavior in such a scenario would be printAction not being called. The only solution I can think of is defning a new "delegate container" class as such:
class ActionContainer
{
public Action Action = new Action(delegate { });
}
private void TestCapturedActionContainer()
{
var actionContainer = new ActionContainer();
Action printAction = () => Console.WriteLine("Printing...");
actionContainer.Action += printAction;
CaptureInvoker(actionContainer);
actionContainer.Action -= printAction;
_captureAction();
}
private void CaptureInvoker(ActionContainer actionContainer)
{
_captureAction = () => actionContainer.Action();
}
This works but I wonder if my desired behavior can be achieved without introducing this new layer of abstraction. Implementing the strategy pattern can easily lead to such a situation, so one would reckon the language and/or the BCL would support it natively somehow.
Thanks !
Delegates are like strings. They're implemented as reference types, but they behave more like immutable value types. When you add or subtract characters on a string, it doesn't change the string, it produces a new string that is the new result. When you add or subtract numbers from an integer, it doesn't change the integer, it produces a new integer that is the new result. And when you add or substract a delegate from a delegate, it doesn't change either delegate; it produces a new delegate which is the result.
If what you want to capture is a delegate which can vary then capture a variable that contains a reference to a delegate. Variables vary, that's why they're called "variables". If you want something that can vary, get the variable.
CaptureActionFromParam(()=>{action();});
Now the delegate that is captured has itself captured the variable "action", not the value that happens to be in it.
Remember:
Parameters are passed by value.
Lambdas capture variables, not values.
Make sense?