Anonymous function and local variables - c#

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.

Related

How do I access a var from another method without calling the method

Hello this is my fist real post on stack overflow so sorry if I didn't do this correctly :D
namespace foobar //not originally named foobar
{
[DesignTimeVisible(false)]
public partial class MainPage : ContentPage
{
int Spinrate = 2000;
int Points = 1;
int clickmulti = 1;
public static object Public { get; private set; }
public MainPage()
{
InitializeComponent();
var gamemusic = Plugin.SimpleAudioPlayer.CrossSimpleAudioPlayer.Current;
var laughtrack = CrossSimpleAudioPlayer.CreateSimpleAudioPlayer();
gamemusic.Load("audioloop.mp3");
laughtrack.Load("wslaugh.wav");
gamemusic.Play();
}
async void bigger(object sender, EventArgs args)
{
Points += Spinrate + (50 * 2 * clickmulti);
Pointdisplay.Text = Convert.ToString(Points);
cmdisplay.Text = Convert.ToString(clickmulti);
if (Spinrate >= 300)
{
Spinrate -= 50;
}
if (Spinrate <= 1000)
{
clickmulti = 2;
}
if (Spinrate <= 500)
{
clickmulti = 3;
}
await img.RelRotateTo(360, (uint)Spinrate);
laughtrack.Play();
}
}
}
I am currently trying to figure out how to call without having to define as either a public
(since when using var it can't for some reason) and without having to define var laughtrack = CrossSimpleAudioPlayer.CreateSimpleAudioPlayer(); in the method bigger
since it creates a lot of latency in the program.
Currently I have tried:
Making laughtrack a public var, but it errors out with:
Error CS0825 The contextual keyword 'var' may only appear within a local variable declaration or in script code foobar as the output
I have defined laughtrack in the class scope and im getting the same error as attempt 1.
I have added the declaration and call for laughtrack in the same method. It seems to cause a lot of latency and even if it isn't the cause, it still is loading it every single time the method is called.
Thanks to all who give this a shot!
you need to declare laughtrack at the class level (not inside a specific method) if you want it to be visible in other methods. But if you do this you can't use var, you have to know the actual type.
ISimpleAudioPlayer laughtrack;
public MainPage()
{
InitializeComponent();
var gamemusic = Plugin.SimpleAudioPlayer.CrossSimpleAudioPlayer.Current;
laughtrack = CrossSimpleAudioPlayer.CreateSimpleAudioPlayer();
gamemusic.Load("audioloop.mp3");
laughtrack.Load("wslaugh.wav");
gamemusic.Play();
}
Your first attempt was great and exactly what you need to do, however var isn't usable outside of functions. Just explicitly write the type of the variables!
When you create a method or let's simply call it a Function, all variables or nested functions you define inside of it are scoped to that particular function or block of code and will not be available outside that scope unless returned or thrown out by that function.
In your case, the standard approach is to define a struct with properties and methods. But, please consider how those properties are initiated. If that is through calling a function, then you will still need to call the function, but it allows for a Singleton behavior.
To further clarify, consider the concept of Pure Functional programming where a Pure Function takes some inputs and returns a single output. In OOP, the method can update global variables (there's no such thing in C#, but C++ and C have those) and scoped properties/variables inside a class or struct as well. Though, you cannot directly access its members.

How can I extract the "real" target from an Event Handler that was defined anonymously?

Follow up to my question here:
According to a comment made -
The compiler creates a class member with a fictitious name and attaches it just as you would attach a declared method.
I do not fully comprehend what this means but I can verify that if instead of saying
Foo.Bar += (S, E) => { /*Code Goes Here*/ }
I instead say
Foo.Bar += FooBar;
private void FooBar( object sender, EventArgs e ){
/*Code Goes Here*/
}
then Event.Target changes from WhatIsThis.App.<>c to WhatIsThis.App.
That's awesome but I can't guarantee that I will always write an actual method to attach to an event handler.
In the cases where I do use an anonymous method, is there a way to extract the real target, or am I just pigeonholed into using defined methods (I mean, I can live with that I guess but if there's some high-tech sorcery that I can employ to extract the real target, then I'm all for it ).
do not fully comprehend what this means
Let's fix your comprehension. Suppose you have:
class C
{
int x;
void M(int y)
{
int z = GetZ();
Func<int, int> f = q => q + x + y + z;
...
f is a delegate. It has a receiver and a method that is a method of the receiver. (Note, this is not actually a strict requirement but the corner case is obscure for our purposes today.)
What type is the receiver that has that method?
Can it be C, with the receiver equal to this? No. Why not? Because then how do we keep track of the value of y and z? There could be a different value for every invocation of M, so the receiver cannot be this.
What the compiler does is generates a new class:
class C
{
int x;
class Locals
{
public C __this;
public int y;
public int z;
public int A(int q) { return q + __this.x + y + z; }
}
void M(int y)
{
Locals locals = new Locals();
locals.__this = this;
locals.y = y;
locals.z = GetZ();
Func<int, int> f = locals.A;
...
So what is the receiver? the value of locals. What is the method? A.
Of course both Locals and A are given crazy names so that you cannot call them by accident.
In the cases where I do use an anonymous method, is there a way to extract the real target
You are extracting the real receiver, I promise.
'm working on an extension to do some things with Event Handlers and I need to be able to discern what an event handlers target is
Please don't do that. The receiver of an event handler is an implementation detail of the code that provided the handler. It's not there for you to make decisions on. Compilers are well within their rights to make any choice they like when generating a receiver for an event handler, and they do. Consider what happens if the event handler was created inside an iterator block, or an async method, for example. Or the event is being subscribed by some reactive extensions code that is applying sequence operations to the event. Again, the compiler will be generating classes all over the place. You can't rely on the class being something "sensible".
The thing you can rely on is: the subscriber wished the given method to be called when something happened. That's the contract you must obey; don't try to second-guess the subscriber.

Get the lambda to reference itself

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.

Calling a function within a listener function

I'm new to the C# world and I am trying to call another function inside a listener using this code below:
private void Form1_Load(object sender, EventArgs e)
{
listener = new GestureListener(100);
listener.onGesture += listener_onGesture;
controller = new Controller(listener);
}
static void listener_onGesture(Gesture gesture)
{
string gestures = "";
foreach (Gesture.Direction direction in gesture.directions) {
gestures = direction.ToString();
}
int howManyFingers = gesture.fingers;
if (gestures == "Left" && howManyFingers == 2) {
test();
} else {
Console.WriteLine("gestured " + gestures + " with " + gesture.fingers + " fingers.");
}
}
private void test()
{
pdf.gotoNextPage();
}
However, it does not seem to work when i do that. The error it gives me on the line test(); is:
An object reference is required for the non-static field, method, or property 'LeapDemoTest.Form1.test()'
How can i do this?
You're seeing this because listener_onGesture is a static method -- meaning, the method is not associated with a given instance of your class. However, test is an instance method -- so it is scoped to the specific instance.
I see three options, depending on the scope of "pdf", but I recommend option 1:
Make listener_onGesture an instance method (remove the static keyword)
Make test a static method -- this will only work if pdf is also a static member.
Somewhat hackish -- find the Form instance that invoked the event by inspecting the sender's properties and invoke the test method on that instance.
listener_onGesture probably shouldn't be static. You want to access instance fields within this method, and you appear to be calling it from within an instance of the application (Form1_Load, where you currently reference it from, is not a static method). By removing the static modifier from that method you will then be able to call a non-static method.

How do outer variables within a loop work with lambdas?

I just wrote some code that I don't quite comprehend how it works.
My question is about the local variables in the for loop which are then referenced when the radio button event occurs.
How does it track the different "versions" of these local variables and operate correctly? (i.e. the resulting radio buttons each fire an event with their corresponding value which is derived from an outer local variable)
public class RadioButtonPanel<T> : FlowLayoutPanel
{
public RadioButtonPanel()
{
foreach (object value in Enum.GetValues(typeof(T)))
{
string name = Enum.GetName(typeof(T), value);
var radioButton = new RadioButton { Text = name };
radioButton.CheckedChanged += (s, e) =>
{
if (radioButton.Checked && this.Selected != null)
Selected((T)Enum.Parse(typeof(T), name));
};
this.Controls.Add(radioButton);
}
}
public event SelectedEvent Selected;
public delegate void SelectedEvent(T t);
}
This is done via a Closure.
Basically you can imagine that a small class has been created for you on your behalf, that has two properties for your local variables, and a single function. When your lambda gets called, it basically news one of those up and calls the function, thus preserving the values given to it.
The C# specification actually has some really good examples of how this is done by the compiler. Specifically section 6.5.3
They're called closures, see Wikipedia: http://en.wikipedia.org/wiki/Closure_(computer_science)
Basically, they allow for the use of non-local variables within the lambdas. From what I can remember, these variables are compiled externally from your function so they can be used globally.

Categories

Resources