How do outer variables within a loop work with lambdas? - c#

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.

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.

Anonymous function and local variables

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.

how to handle events from variable number of sources?

I have such class (sorry about posible mistakes, i'm writing it right here.) Class is simplified for this example, it must be more complex of course.
class SP500Index {
SP500Index(List<OrderBook> stocks) {
foreach (var stock in stocks) {
stock.StockUpdated += stockUpdated; // how to handle?
}
}
}
So I have a lot of sources and I need to handle StockUpdated event from them. In handler I need to know index of stock in stocks list which raised the event. How to do that?
upd for perfomance reasons I don't want "sender look-up" instead I want index. Lookup is not trivial operation and likely involves Hashcode calculation Equals method call etc. Imagine how ofthen SP500 index changes...
This is not provided automatically.
But the StockUpdated event should look like
void StockUpdated (object sender, MyEventArgs e)
and you can cast senderto a stock and look it up in the original list. If you still need the index.
void stockUpdated (object sender, MyEventArgs e)
{
OrderBook stock = (OrderBook) sender;
....
}
It is good practice to use a signature of the form:
public delegate void CustomEventHandler(object sender, CustomEventArgs a);
In your event handler you can use sender to find out which object raised the event.
If you don't have a sender parameter then I don't think there is any (reasonable) way to find out which object raised the event.
Related
How to: Publish Events that Conform to .NET Framework Guidelines (C# Programming Guide)
If your benchmark shows that you need the index when the event occurs you can add the index as a property to OrderBook and when you add an element to the list, set this property. This value will be available to the event handler.
This will work assuming that you keep the OrderBook objects in a single List and do not do any rearranging of this original list. If you have multiple lists with each object stored on only one of those lists, then you could add an Owner property that references the list that it is stored on.
For example...
When you build List<OrderBook>:
...
OrderBook book = CreateOrderBook(...);
list.Add(book);
book.ListIndex = list.Count - 1;
// Assign Owner here if that is needed.
...
Or better, use a helper for managing the list that cares for the book keeping of the index update:
public class OrderBookManager
{
private List<OrderBook> list = new List<OrderBook>();
public void Add(OrderBook book)
{
list.Add(book);
book.ListIndex = list.Count - 1;
// Assign Owner here if that is needed.
}
// Make this read-only if you want to ensure the manager controls all updates to the list (better design) but use it this way for higher performance.
public List<OrderBook> List { get { return list; } }
}
The updated OrderBook:
public class OrderBook
{
...
public int ListIndex { get; set; }
}
And then an sample event handler using this index:
public void StockUpdated(object sender, MyEventArgs eventArgs)
{
OrderBook book = (OrderBook) sender;
//Here use book.ListIndex to access the original list element.
}
What is your reason for having that index? Everything you need should be in the object. If you use this to manipulate the original list (such as removing this item from the list) then you have the problem of recomputing the saved index of all the previously stored objects.
If you are maintaining a parallel list with other objects, then you perhaps should consider a different design.
You can define in delegate of that event sending source (which is basically the suggested guideline by Microsoft. Having object sender, in other words, like a first parameter of the delegate's signature).
After make (say) a cast and determine in some if/else the real object type.

Assigning a value to a global variable from inside of event handler?

I'm working on a program but an issue i was faced to keep me worried.I'm kind of novice and i'm building this program for a competition.The code where the problem lies is like following :
class Blabla : Usercontrol
{
public List<string> mainList;
public Blabla()
{
mainList = new List<string>();
something.DownloadStringCompleted += new DownloadStringCompletedEventHandler(xx_DownloadStringCompleted);
}
void xx_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
List<string> abc = SomeMethod(e.Result);
mainList = abc;
}
}
I try it.Even though "abc" variable has the value i want , mainList remains empty.I don't know why and how to make it work.That's why i need some hand.Thank you.
Variable abc has the value you want UNTIL you get out of your event handler, probably, when it gets deleted because it uses e.Result directly.
Familiarize yourself with .Clone() method and IClonable interface, and try creating a COPY of the list that is in question, not the reference.
If abc is a list, mainList will be set to the same list. You don't have to clone the list, it should stay active because there is a reference to it, and therefore it doesn't get garbage collected.
When you said that mainList was empty, did you look at it in the debugger immediately after setting it in the xx_DownloadStringCompleted method? Or are you looking at it somewhere else in your program?
I would guess that this is a threading issue. Does your event handler get called from a different thread? If so, you would need to add some synchronization logic in order to guarantee that mainList is available to your other thread.

Initializing events with initializer syntax

I often want to write something like this:
new Form
{
Text = "Caption",
Controls =
{
new Button { Text = "Button 1", Click = (s, e) => MessageBox.Show("Button 1 Clicked"), Location = new Point(10, 10) },
new Button { Text = "Button 2", Click = new EventHandler(Button2Clicked), Location = new Point(10, 40) },
new Button { Text = "Button 3", Click = Button3Clicked, Location = new Point(10, 70) },
},
}
Initializer syntax is just sugar, so why can't the compiler figure out how to generate code for an event subscription?
Gimme some sugar, baby!
When initializer syntax was invented, someone must have thought about events and rejected them. I've been trying to imagine what the rationale might have been and am coming up blank.
Is it because an event is a multi-cast object that might have more than one subscriber? No, this is an initialization process; There can be no other subscribers. [Updated] Not true, initializers are applied post-construction and an object can subscribe to its own events.
A note to Eric: I've heard the Why doesn't C# implement feature X speech. In this case, someone was already there, implementing initializers.
Updated
There seems to be contention/confusion because I used Click = in my example. The actual syntax is not relevant to the question. It could just as easily be Click += which mirrors the way you have to add a handler normally. I prefer the former because it's consistant with the rest of the initializer syntax, but ultimately I don't care, just so long as I can subscribe to an event in an initializer list.
Another Update
I do realize that adding the feature now is probably unlikely. The first issue that comes to mind is that Intellisense has to be updated. There are probably many other things that would hinder adding this feature now. My question is: Why didn't they add it in the first place. There must have been something compelling that warrented the 'nay' vote.
I cannot see any reason why they could not have provided this small teaspoon of sugar, I guess they just didn't!
There is already quite a lot of syntactic sugar involved in events, if simply declare an event on a class without providing your own implementation, the compiler is providing a delegate backing field for you, plus add / remove 'method' implementations. ALso, when you add an event handler, the compiler uses delegate inference, allowing you to simply point to a method, rather than create a delegate that represents the method.
Interestingly, Mono C# does allow you to add an event handler in an object initializer:
http://tirania.org/blog/archive/2009/Jul-27-1.html
Time to switch to Mono ;-)
Try simply assigning an event:
Click = (o,e) => { <CODE> }
Doesn't work. Initializers work only with things you can directly assign like that. This is because events need to be able to notify anyone they want (you shouldn't be allowed to remove someone else's registration for that event on accident).
I'm not sure if this is their reasoning, but it works for me.
There's a big difference between fields and events. There's an excellent article here outlining the differences, but that's the answer to your question: A field can be assigned a value; an event looks like a field but is a very different beast.
Edit
From the article I linked to:
We have seen that the event keyword is a modifier for a delegate declaration that allows it to be included in an interface, constrains its invocation from within the class that declares it, provides it with a pair of customizable accessors (add and remove), and forces the signature of the delegate
Remember that event is a shortcut; behind the scenes, the compiler creates an object with add() and remove() methods. Like:
public class Button {
public event EventHandler Click {
void add {...}
void remove {...}
}
}
Perhaps this will offer some insight... :
Button btn = new Button {Click += (s, e) => MessageBox.Show("hello")};
The error message you get is "Cannot initialize type 'Button' with a collection initializer because it does not implement IEnumerable"
Still another note... if you assign the event handler from within the form, you can do this:
this.button1.Click += (s, e) => this.textBox1.Text = e.ToString();
You couldn't access form variables from the code you've created. I get where you're coming from, and I don't disagree... what you're doing could be made to work. I guess my point is that there are reasons why the decision was made not to make it work.
Yep, should be part of the language!
But, here's a tricky workaround that lets you subscribe to events within an initializer list...
public class TestClass
{
public class MyButton : Button
{
public EventHandler ClickSubscriber
{
get { return null; }
set { Click += value; }
}
}
public static void RunTest()
{
new Form
{
Text = "Caption",
Controls =
{
new MyButton
{
ClickSubscriber = (s, e) =>
MessageBox.Show("Button 1 Clicked"),
},
},
};
}
}

Categories

Resources