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?
Related
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 );
I have this code;
Button button = new Button();
MessageBox ms = new MessageBox(button);
Action<bool> action = ms.Show();
action += (b) =>
{
Console.WriteLine(b.ToString()); //this isnt working
Console.WriteLine("??");
};
Console.Read();
button.OnClick();
Console.ReadKey();
MessageBox class :
class MessageBox
{
Button _button;
public MessageBox(Button button) { _button = button; }//initialize button
public Action<bool> Show()
{
Action<bool> action = new Action<bool>(CallForTest);
_button.OnClick+=()=>{ action?.Invoke(true); };
return action;
}
//...working.*//
public void CallForTest(bool statu){}
}
I want to return an action and when button is clicked,call the action.But this isnt working? What is the problem? Action is a delegate so delegate is a reference type?(compiler generated class) What is wrong in this picture?
I think when "Show()" is ends,"action" is collected from gargabe collector.But this is working with other reference types? for example;
public Test Show()
{
Test test = new Test("??");
button.OnClick += () =>
{
test.JustForTest(); //working (cw("?????" + ctorvalue);
};
return test;
}
Delegates are immutable. When you are combining two delegates using +=, you are actually creating a new delegate. So when you have done act += ... in the above code, you have actually created a new delegate, it is different from what you have already created in Show() method.
I believe this is happening because when you use += to a delegate it does not append to the internal list. This is why you don't see b.string() being printed
Without changing your design you won't be able to append the action to the original delegate when the button is clicked.
What you are actually writing is somthing like:
var act2 = new Action<bool>((b) =>
{
Console.WriteLine(b.ToString()); //this isnt working
Console.WriteLine("??");
});
var act = act + act2;
as you can see act is getting a new reference to the combined expression of act + act2 rather than act itself concatenating act2 internally.
if you do act(false) you will see the extra results, but not if you invoke the button click.
What you should be using is event on the delegate within the Button, which is the way UI controls are written
class Button
{
public event EventHandler<BoolEventArgs> Click;
}
best to read up on using events when you want to have multicast delegates in this way. MSDN site
This question already has answers here:
C# - Event keyword advantages?
(3 answers)
Closed 6 years ago.
I detected the feature of C# that it is possible to use an Action or Func like an event. What I mean is, that I can do following:
Action aAction;
aAction = DoSomething;
aAction += DoAnotherting;
// execute the action -> both functions will be executed
aAction();
aAction -= DoSomething; // unsubscribe on function
I was not aware of this, thought using += is only possible for events. In the first moment this looks quite well, as I do not have to use the event keyword and I can also call this action from outside of the owner class (what is not possible for events).
But I'm wondering, are there any good examples for such a use or is it only bad practice?
A complete example is shown here:
[TestMethod]
public void DummyTest()
{
DummyClass myInstance = new DummyClass();
int i = 0;
Action action1 = () => i++;
Action action2 = () => i += 2;
Func<int> func1 = () => 5;
myInstance.MyFunc += () => 3;
myInstance.MyFunc += func1;
Assert.AreEqual(5, myInstance.MyFunc?.Invoke() );
myInstance.MyFunc -= func1;
Assert.AreEqual(3, myInstance.MyFunc?.Invoke() );
myInstance.MyAction = action1;
myInstance.MyAction += action2;
myInstance.MyAction?.Invoke();
Assert.AreEqual(3, i);
myInstance.MyAction -= action1;
myInstance.MyAction?.Invoke();
Assert.AreEqual(5, i);
myInstance.MyAction = () => i = 0;
myInstance.MyAction?.Invoke();
Assert.AreEqual(0, i);
}
class DummyClass
{
public Action MyAction;
public Func<int> MyFunc;
}
It is my impression that the whole point of events is to put the control of events into the enclosing type. It is not up to clients to choose when the event is fired. An event is a (collection of) function(s) that is(/are) invoked when some state is changed in a type or when something interesting happens that clients might want to react to, but the exact details should be kept hidden for the same reason that you shouldn't expose fields to clients either.
There's nothing inherently HORRIBLE with it in the sense that it's going to blow up your house, but on the other hand there is no REASON to use them like this. Events are in the language for a reason, they have semantic meaning. If you use Action/Func delegates instead of events, people who read your code will have to figure out what the heck you're doing and why you aren't using the conventional tools instead. It's just clutter/noise, so my advise is to avoid it.
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 am trying to make lambda able to reference to itself, an example:
PictureBox pictureBox=...;
Request(() => {
if (Form1.StaticImage==null)
Request(thislambda); //What to change to the 'thislambda' variable?
else
pictureBox.Image=Form1.StaticImage; //When there's image, then just set it and quit requesting it again
});
When I tried to put the lambda in variable, while the lambda referenced to itself, error of course.
I thought about creating class with a method that able to call itself, but I want to stick here with lambda. (While it gives only readibility so far and no advandges)
You need to declare the delegate, initialize it to something so that you are not accessing an uninitialized variable, and then initialize it with your lambda.
Action action = null;
action = () => DoSomethingWithAction(action);
Probably the most common usage I see is when an event handler needs to remove itself from the event when fired:
EventHandler handler = null;
handler = (s, args) =>
{
DoStuff();
something.SomeEvent -= handler;
};
something.SomeEvent += handler;
As of C# 7, you can also use local functions:
PictureBox pictureBox=...;
void DoRequest() {
if (Form1.StaticImage == null)
Request(DoRequest);
else
pictureBox.Image = Form1.StaticImage; //When there's image, then just set it and quit requesting it again
}
Request(DoRequest);
Here is an interesting post on the subject from the experts -
http://blogs.msdn.com/b/wesdyer/archive/2007/02/02/anonymous-recursion-in-c.aspx
Excerpt from the post -
"A quick workaround is to assign the value null to fib and then assign the lambda to fib. This causes fib to be definitely assigned before it is used.
Func<int, int> fib = null;
fib = n => n > 1 ? fib(n - 1) + fib(n - 2) : n;
Console.WriteLine(fib(6)); // displays 8
But our C# workaround doesn't really use recursion. Recursion requires that a function calls itself."
Read the entire post, if you are looking for other fun ways of doing it.