Click here to Skip to main content
15,880,392 members
Articles / Programming Languages / C#

Comparing Normal Code, Rx and YieldAwait

Rate me:
Please Sign up or sign in to vote.
4.80/5 (10 votes)
27 Jan 2011BSD5 min read 31.8K   229   16   9
Comparing the code styles when you use a normal event handler, Rx (Reactive Extensions) and YieldAwait library.
Screenshot of the Application Above

ComparingYieldAwait/app_screenshot.gif

What is Described in This Article?

We have several ways to await events in C#, for example:

  1. using a normal event handler
  2. using a lambda function as an event handler
  3. using Rx (Reactive Extensions)
    I've created a library named as YieldAwait that allows you to stop running the code wherever you want in order to await events using the functionality of yield sentence. It means that it provides you with a new way of awaiting events. So we've had yet another way now.
  4. using YieldAwait library

Therefore, in this article, I'm going to compare what the code styles look like when you use those four ways, respectively.

What is YieldAwait Library?

YieldAwait library is being developed in CodePlex. You can download the source codes and more examples there. It includes more advanced examples of awaiting events using this library.

Assuming an Example Situation

As an example here, we think about the situation where we create a code which blinks the backcolor of a Label UI control by awaiting Timer.Tick events. The blinking is in the way like black -> gray -> white -> gray -> black...

The final goal of the blinking behavior should be like the following image:

ComparingYieldAwait/blinking.gif

For the comparison, we use the same default form layout which contains one Label UI control label1 and one Timer control timer1 as shown below:

ComparingYieldAwait/design_form.png

ComparingYieldAwait/doc_outline.png

1. Using a Normal Event Handler

First, here is the code to blink the backcolor of a Label UI control by awaiting Timer.Tick events by using a normal event handler in the line [C].

C#
 public partial class NormalEventHandlerForm : Form {

    public NormalEventHandlerForm() {
        InitializeComponent();
    }

    private int _Bright = 0; // [A]
    private int _Step = 10; // [B]

    private void timer1_Tick(object sender, EventArgs e) { // [C]

        _Bright += _Step; // [D]
        if (_Bright > 255) { // [E]
            _Step = -10;
            _Bright += _Step;
        } else if (_Bright < 0) {
            _Step = 10;
            _Bright += _Step;
        }

        label1.BackColor = Color.FromArgb(_Bright, _Bright, _Bright); // [F]
    }
} 

In this case, you have to use two field variables of _Bright and _Step to keep the color blinking information among each Timer.Tick event. This code seems to be very typical and we write this way in a normal event-driven programming.

2. Using a Lambda Function as an Event Handler

In this section, a lambda function is used for the event handler in the line [C], which makes it different from the section 1 (event handler).

C#
public partial class LambdaEventHandlerForm : Form {

    public LambdaEventHandlerForm() {
        InitializeComponent();
    }

    private void LambdaEventHandlerForm_Load(object sender, EventArgs e) {

        var bright = 0; // [A]
        var step = 10; // [B]

        timer1.Tick += (_sender, _e) => { // [C]

            bright += step; // [D]
            if (bright > 255) { // [E]
                step = -10;
                bright += step;
            } else if (bright < 0) {
                step = 10;
                bright += step;
            }

            label1.BackColor = Color.FromArgb(bright, bright, bright); // [F]
        };
    }
} 

In this case, the code style is very similar to the section 1 (event handler), but you don't have to define field variables.

3. Using Rx (Reactive Extensions)

We can also use the power of Rx (Reactive Extension) for this example. Rx is useful in the case of awaiting Timer.Tick events repeatedly as well.

C#
public partial class RxForm : Form {

    public RxForm() {
        InitializeComponent();
    }

    private void RxForm_Load(object sender, EventArgs e) {

        var enum_bright = Enumerable // [G]
            .Range(0, 25 + 1)
            .Concat(Enumerable
                .Range(0, 25)
                .Reverse())
            .Select(_i => _i * 10)
            .ToArray();

        Observable
            .FromEvent<EventArgs>(timer1, "Tick") // [H]
            .Zip(enum_bright, (_e, _bright) => _bright) // [I]
            .Repeat()
            .Subscribe(_bright => {
                label1.BackColor = Color.FromArgb(_bright, _bright, _bright); // [F]
            });
    }
} 

The code style is very different from the previous sections as you will see. It would be easy to understand how the code behaves if you have somewhat experience in using Rx.

In the line [G], an array named as enum_bright containing the list of [0, 10, ... 240, 250, 240, ... 10, 0] is created. Using Observable of Rx, the timer1.Tick event is caught in the line [H] and the array and the events are combined in the line [I]. Each time the event occurs, the code in the line [F] will be called and the variable _bright will change like 0, 10, ... 240, 250, 240, ... 10, 0. Thus, the back color of label1 will blink from black to white to black.

4. Using YieldAwait Library

In this section, YieldAwait library is used. The library is very useful in such a type of cases where you have to await several events in a sequential way.

C#
public partial class YieldAwaitForm : Form {

    public YieldAwaitForm() {
        InitializeComponent();
    }

    IEnumerable<bool> TestFunc(EventWaiter waiter) {

        while (true) { // [J]

            for (var bright = 0; bright < 255; bright += 10) { // [K1]
                label1.BackColor = Color.FromArgb(bright, bright, bright); // [F1]
                yield return waiter.Wait(timer1, "Tick"); // [L1]
            }

            for (var bright = 255; bright > 0; bright -= 10) { // [K2]
                label1.BackColor = Color.FromArgb(bright, bright, bright); // [F2]
                yield return waiter.Wait(timer1, "Tick"); // [L2]
            }
        }
    }

    private void YieldAwaitForm_Load(object sender, EventArgs e) {
        new EventWaiter(TestFunc);
    }
} 

It's straight-forward. You can express what you want to do directly in the code. You can write codes exactly in the same way as you thought. You can do that even if you don't know how to use Rx effectively.

The code has two for loops. One in the line [K1] is for the blinking from black to white and the other in the line [K2] is for the blinking from white to black. In the [K1] for loop, the variable bright will go from 0 up to 250 like 0, 10, ... 240, 250. The for loop will stop at the line [L1] each time and run again when a timer.Tick event occurs. Thus, the back color of label1 will change from black to white. The [K2] for loop works in the same way and the [J] while loop makes the blinking last forever.

If you want to know how to use this library, the project page on CodePlex has an easy-to-understand explanation and a very simple example code. So please go to see the page also.

5. Using a Normal Event Handler with Yield Function

After looking at both the code styles of the sections 3. (Rx) and 4. (YieldAwait), you might think it is also possible to use a normal event handler with a yield function in the following way:

C#
public partial class EventHandlerAndYieldForm : Form {

    public EventHandlerAndYieldForm() {
        InitializeComponent();
    }

    private IEnumerator<int> _BrightEnumerator;

    private void timer1_Tick(object sender, EventArgs e) { // [C]

        _BrightEnumerator.MoveNext();
        var bright = _BrightEnumerator.Current;
        label1.BackColor = Color.FromArgb(bright, bright, bright); // [F]
    }

    IEnumerator<int> _GetBrightEnumerator() {

        while (true) {

            for (var bright = 0; bright < 255; bright += 10) {
                yield return bright;
            }

            for (var bright = 255; bright > 0; bright -= 10) {
                yield return bright;
            }
        }
    }

    private void EventHandlerAndYieldForm_Load(object sender, EventArgs e) {
        _BrightEnumerator = _GetBrightEnumerator();
    }
} 

I think it's less readable comparing to the section 4 (YieldAwait), but you can also write code this way using a yield function.

Points of Interest

First, you can write code in many styles for the blinking label program. I can't say which is good and which is bad. All the styles might have good points and bad points.

However, some points that I thought are interesting are:

  • When you use Rx like in the section 3,
    • many small lambda functions are likely to appear in code,
    • and the scope of each variable that is used in the code is likely to be small.
    • The small scope means being less complex and easy-to-read.
  • When you use the YieldAwait library like in the section 4,
    • you don't use any lambda functions,
    • and the code looks very common because of not using any technical coding (like method chain or lambda functions...) but yield sentences.
    • It gets more readable because the code will run exactly in the order of what is just written, line by line.
    • The usage and behavior are very similar to using the new keyword await introduced in the next version C# 5.0.

What do you think the points are? Your comments are welcomed.

Links and More Information

Project

Technical Explanations

Previous Forums

History

  • 2011/01/10
    • Wrote an article of the first edition
  • 2011/01/18
    • Added a few explanations to the sections 4 and 5
    • Added a screenshot for the sample application
  • 2011/01/19
    • Added a few explanations to Points of Interest
  • 2011/01/28
    • Added related links

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Japan Japan
Homepage: http://simonpg.web.fc2.com/Pages/index.html

Comments and Discussions

 
GeneralMy vote of 5 Pin
RaviRanjanKr29-Jan-11 3:27
professionalRaviRanjanKr29-Jan-11 3:27 
QuestionHow about a more complex example? Pin
Qwertie25-Jan-11 10:00
Qwertie25-Jan-11 10:00 
AnswerI will try to do that while I'm writing the next article. Pin
Simon.P.G.26-Jan-11 0:49
Simon.P.G.26-Jan-11 0:49 
GeneralMy vote of 5 Pin
Vishal.Doshi24-Jan-11 6:16
Vishal.Doshi24-Jan-11 6:16 
GeneralC# 5.0 Aync/await Pin
Andrew Rissing24-Jan-11 4:08
Andrew Rissing24-Jan-11 4:08 
GeneralActually, I got the idea when yield keyword was introduced into C# [modified] Pin
Simon.P.G.25-Jan-11 1:31
Simon.P.G.25-Jan-11 1:31 
GeneralRx and YieldAwait Pin
andreas.harold7519-Jan-11 4:36
andreas.harold7519-Jan-11 4:36 
GeneralMy vote of 5 Pin
Marc Clifton19-Jan-11 2:33
mvaMarc Clifton19-Jan-11 2:33 
GeneralI'm planning to write another example now. Pin
Simon.P.G.19-Jan-11 10:14
Simon.P.G.19-Jan-11 10:14 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.