In this tutorial you will learn more core C# and a look at delegates and events. This will allow you to write a simple C# program that you will then compile. The program will be a Visual Studio Form application
About this Tutorial
Objectives
Delegates will learn to develop applications using C# 4.5. After completing this course, delegates will be able to:
Use Visual Studio 2012 effectively
Create commercial C# Web Applications
Develop multi-threaded applications, Use WCF and LINQ
Audience
This course has been designed primarily for programmers new to the .Net development platform. Delegates experience solely in Windows application development or earlier versions of C# will also find the content beneficial.
Prerequisites
No previous experience in C# programming is required. But any experience you do have in programming will help. Also no experience in Visual Studio is required. But again any experience you do have with programming development environments will be a valuable.
Experience using a contemporary OO language such as C++ or C# would be useful but is not required.
In this tutorial you will be learning about single-cast delegates and multicast delegates that allow much greater functionality to the programmer by allowing one instance but many possible methods. You will also be learning about how to create anonymous methods which effectively are delegates but with a body and parameters. Next will be lambda expressions which allow the programmer you to easily write these anonymous methods in a one line statement. After this will be working on using events which you have most likely used before.
Estimated Time – 1 Hour
Not what you are looking? Try the next tutorial – Generics
Lab 1: Delegates
Lab 1: Delegates
Overview
A delegate is a .NET data type that acts as a
type-safe pointer to a method on an object/class:
A delegate specifies the signature of methods you would like to call through the delegate, e.g. delegate double TrigOp(double radians);
In your client code, create a delegate instance and bind it to any method (on any object/class) that has the correct signature – TrigOp op = new TrigOp(Math.Sin);
In your client code, you can use the delegate to call the method when you’re ready – double result = op(an-angle-in-radians);
Lab
Open visual studio, Create a New Project>C#>Windows>Form Application and call this TrigDelegates, this is going to use delegates to allow us to write only one main method easily instead of having a large multi layered if statement. What this is going to work is what the value of a trigonometric angle is –
Then you need to create a windows form that looks similarly to what is below – name the buttons btnSin, btnCos, btnTan and txtDegrees –
Add this code to Form1.cs – We create a delegate that will be able to hold any method that accepts a double as its input. In this case it will be either Sin, Cos or Tan. To start we well create the event handlers for when the buttons are pressed – delegate double TrigOp(double radians);
private void btnSin_Click(object sender, EventArgs e)
{
TrigOp op = new TrigOp(Math.Sin);
MyPerformOp(op);
// You can also not mention the delegate object at all....
// MyPerformOp(Math.Sin);
}
private void btnCos_Click(object sender, EventArgs e)
...
}
Now lets create our logic to actually work out the maths behind our program – // Perform the operation indicated by "op"
private void MyPerformOp(TrigOp op)
{
// Test if the text box is empty
if (this.txtDegrees.Text.Length == 0)
{
// Display error message
MessageBox.Show(
...
}
// Get the angle entered in the text field
double degrees = 0.0;
try
{
degrees = double.Parse(this.txtDegrees.Text);
}
catch (FormatException)
{
MessageBox.Show(
....
}
// Convert the angle from degrees into radians
double radians = (degrees / 360) * 2 * Math.PI;
//*******************
// Invoke function specified by the "op" delegate
double result = op(radians);
...
}
Now you have finished feel free to run the application and test its functionality; maybe
.NET supports two distinct kinds of delegate:
Single-cast delegates.
Multicast delegates.
Why use multicast delegates?
If you need to perform a series of operations on the same object.
If you need to perform an operation on a series of different objects.
Example Of a Multi Cast delegate:
Let’s see an example of how to define and use multicast delegates – See the MulticastDelegates demo project.
The sample has a main form, which allows the user to create child forms – The user can then change the colour of the child forms and it uses a multicast delegate to invoke a Repaint() method on each child form
The main difference with this is we create a list of delegates that are all linked to the repaint method allowing us to repaint all the children windows on one button click – // Create a new delegate for the child form's Repaint method.
MyDelegate newDelegate = aChildForm.Repaint;
// If multicast delegate is null, this is the first child form.
if (mTheDelegate == null)
{
// Use new delegate as the basis for the multicast delegate.
mTheDelegate = newDelegate;
sbStatus.Text = "Created first child form.";
}
else
{
// Combine new delegate into the multicast delegate.
// Equivalent to: mTheDelegate = Delegate.Combine(mTheDelegate, newDelegate);
mTheDelegate += newDelegate;
// Use multicast delegate to count the child forms.
sbStatus.Text = "Created child form " + mTheDelegate.GetInvocationList().Length + ".";
}
You must call anonymous methods through a delegate.
You cannot call anonymous methods directly – Internally, the compiler converts anonymous methods into named methods in IL code.
Outer Variables:
An anonymous method can access variables declared in the outer scope – These are called “outer variables”.
When an outer variable is referenced by an anonymous method, the outer variable is said to be “captured” – The outer variable will not be garbage collected until all delegates referencing the anonymous method become eligible for garbage collection
Consider this example –
MyMethod declares a local variable, myLocalVar.
MyMethod returns anonymous method (via a delegate).
Outside world can call anonymous method via delegate – public delegate int MyDelType(); AnonMethodsOuterVariablesDemo.cs
public class MyClass
{
public static MyDelType MyMethod()
{
int myLocalVar = 0;
return delegate { return ++myLocalVar; };
}
}
MyDelType d = MyClass.MyMethod();
Console.WriteLine(d());
Console.WriteLine(d());
Here’s a delegate type that takes a single parameter – delegate void MyHandlerOneParam(int i);
MyHandlerOneParam del1 = i => Console.WriteLine("Via del1, value is {0}.", i);
MyHandlerOneParam del2 = (i) => Console.WriteLine("Via del2, value is {0}.", i);
MyHandlerOneParam del3 = (int i) => Console.WriteLine("Via del3, value is {0}.", i);
Also encapsulate the task of notifying event receivers when an event is raised.
Lab
How to Define and Use Events – Steps to Take:
Define an ‘event argument’ class, to hold context information when an event is raised.
Define a delegate type, to specify the signature of the event (i.e. event handlers must have this signature).
In your event-source class, publish events using the event keyword.
In the event-source class, raise events when something interesting happens.
In the client code, subscribe to the events you’re interested in – Provide event-handler methods, these event-handler methods will be called when the event is raised.
We are now going to create a bank account that enables the user to deposit and withdraw money using lamdas and events.
Open Visual Studio; New Project>C#>Windows>Console Application and create a console application with the name BankSystem –
To start lets create a new class called BankAccount and in this class add our business logic, Some constructors for our class and some events. First in the new class we will create the a transaction event that can be called from another class to return the transaction details. It will create a new transaction each time its called and has an Override for ToString() to return the information to the user – public class BankAccountEventArgs : EventArgs
{
private double transactionAmount;
private DateTime timestamp;
public BankAccountEventArgs(double transactionAmount)
{
this.transactionAmount = transactionAmount;
timestamp = DateTime.Now;
}
public override string ToString()
{
return string.Format("Transaction amount {0}, timestamp {1}", transactionAmount, timestamp);
}
}
Now to add to the same class BankAccount is our BankAccount constructors etc… This will contain one constructor to create our bank account and then we will have three methods. One to withdraw, one to deposit money and the other is a ToString() override. We also create two events one to handle being overdrawn and one to a protection limit which can be raised in our two methods using money – public class BankAccount
{
// Fields.
private string accountHolder;
private double balance;
// Events.
public event EventHandler<BankAccountEventArgs> ProtectionLimitExceeded;
public event EventHandler<BankAccountEventArgs> Overdrawn;
// Methods etc.
public BankAccount(string accountHolder)
{
this.accountHolder = accountHolder;
this.balance = 0;
}
Now adding the methods for deposit and withdraw with a ToString() method in there as well so we can display our information in a customized manner. public void Deposit(double amount)
{
balance += amount;
// If balance has exceeded the government's protection limit, raise a ProtectionLimitExceeded event.
if (balance >= 50000 && ProtectionLimitExceeded != null)
{
ProtectionLimitExceeded(this, new BankAccountEventArgs(amount));
}
}
public void Withdraw(double amount)
...
}
Now the class has been created we will add the code to the Program.cs to show it working in action. What will happen is we are setting up three different methods to handle either an overdrawn event or protection limit event and will raise an event to show these in action. First of all create a new account – BankAccount acc1 = new BankAccount("Brendan");
Then we will create our first way to deal with the events. This is using normal event handlers to start – // Handle events using verbose (or shorthand) syntax.
acc1.ProtectionLimitExceeded += new EventHandler<BankAccountEventArgs>(My_ProtectionLimitExceeded_Handler);
acc1.Overdrawn += My_Overdrawn_Handler;
// AND THE METHODS TO DEAL WITH THEM
static void My_ProtectionLimitExceeded_Handler(object sender, BankAccountEventArgs e)
{
Console.WriteLine("ProtectionLimitExceeded handler method, eventargs {0}.", e);
}
static void My_Overdrawn_Handler(object sender, BankAccountEventArgs e)
{
Console.WriteLine("Overdrawn handler method, eventargs {0}.", e);
}
The second way is to use the anonymous methods that we learnt about in lab two. All of these methods for all three ways are all referencing the BankAccountEventArgs that was created when we made the BankAccountEventArgs class and are getting their data using the overriding ToString() method –
// Handle events using anonymous methods.
acc1.ProtectionLimitExceeded += delegate(object sender, BankAccountEventArgs e)
{
Console.WriteLine("ProtectionLimitExceeded anon method, eventargs {0}.", e);
};
acc1.Overdrawn += delegate(object sender, BankAccountEventArgs e)
{
Console.WriteLine("Overdrawn anon method, eventargs {0}.", e);
};
For our last way of handling an event we will use lambda expressions as learnt about in lab two – // Handle events using lambda expressions.
acc1.ProtectionLimitExceeded += (sender, e) => Console.WriteLine("ProtectionLimitExceeded lambda, eventargs {0}.", e);
acc1.Overdrawn += (sender, e) => Console.WriteLine("Overdrawn lambda, eventargs {0}.", e);
when we run the application we will get 3 different event handlers occurring from the one event – // Do some stuff.
acc1.Deposit(10000);
acc1.Deposit(30000);
acc1.Deposit(40000);
acc1.Withdraw(1000000);
Console.ReadLine();
If you liked this post, please comment with your suggestions to help others.
If you would like to see more content like this in the future, please fill-in our quick survey.
Manage cookie consent
You can view this website without consenting to extra cookies. However you will need to accept the 'marketing' cookies to send messages via the contact forms & see any maps displayed on the site
Functional
Always active
Cookies necessary for the website to work.
Preferences
The technical storage or access is necessary for the legitimate purpose of storing preferences that are not requested by the subscriber or user.
Statistics
The technical storage or access that is used exclusively for statistical purposes.Cookies used to track user interaction on the site, which helps us to evaluate & improve the website.
Marketing: Forms & additional content (Marketing)
We need your permission to place ‘marketing’ cookies, so you are able to use the contact forms & see embedded content e.g. videos and maps. - - - - We have added protection to our contact forms to help prove that a human (rather than a spambot) is filling
If you would like to see more content like this in the future, please fill-in our quick survey.