Sunteți pe pagina 1din 43

DELEGATES

A delegate is a type-safe object that can point to another method (or possibly multiple methods) in the
application, which can be invoked at later time.

Delegates also can invoke methods Asynchronously.

A delegate type maintains three important pieces of information :

1. The name of the method on which it make calls.


2. Any argument (if any) of this method.
3. The return value (if any) of this method.

Defining a Delegate in C#

when you want to create a delegate in C# you make use of delegate keyword.

The name of your delegate can be whatever you desire. However, you must define the delegate to match
the signature of the method it will point to. For example the following delegate can point to any method
taking two integers and returning an integer.

public delegate int DelegateName(int x, int y);

PROGRAM 1 :A Delegate Usage Example

namespace MyFirstDelegate

//This delegate can point to any method, taking two integers and returning an integer.

public delegate int MyDelegate(int x, int y);

//This class contains methods that MyDelegate will point to.

public class MyClass

public static int Add(int x, int y)

return x + y;

public static int Multiply(int x, int y)

{
return x * y;

class Program

static void Main(string[] args)

//Create an Instance of MyDelegate that points to MyClass.Add().

MyDelegate del1 = new MyDelegate(MyClass.Add);

//Invoke Add() method using the delegate.

int addResult = del1(5, 5);

Console.WriteLine("5 + 5 = {0}\n", addResult);

//Create an Instance of MyDelegate that points to MyClass.Multiply().

MyDelegate del2 = new MyDelegate(MyClass.Multiply);

//Invoke Multiply() method using the delegate.

int multiplyResult = del2(5, 5);

Console.WriteLine("5 X 5 = {0}", multiplyResult);

Console.ReadLine();

}
}

Delegate ability to Multicast

Delegate's ability to multicast means that a delegate object can maintain a list of methods to call, rather
than a single method.
if you want to add a method to the invocation list of a delegate object , you simply make use of the
overloaded += operator, and if you want to remove a method from the invocation list you make use of the
overloaded operator -= .
Note: The Multicast delegate here contain methods that return void, if you want to create a
multicast delegate with return type you will get the return type of the last method in the invocation list.

PROGRAM 2 : A Multicast Delegate Example

namespace MyMulticastDelegate

//this delegate will be used to call more than one

//method at once

public delegate void MulticastDelegate(int x, int y);

//This class contains methods that MyDelegate will point to.

public class MyClass

public static void Add(int x, int y)

Console.WriteLine("You are in Add() Method");

Console.WriteLine("{0} + {1} = {2}\n", x, y, x + y);

public static void Multiply(int x, int y)

Console.WriteLine("You are in Multiply() Method");

Console.WriteLine("{0} X {1} = {2}", x, y, x * y);

class Program

static void Main(string[] args)

//Create an Instance of MulticastDelegate


//that points to MyClass.Add().

MulticastDelegate del = new MulticastDelegate(MyClass.Add);

//using the same instance of MulticastDelegate

//to call MyClass.Multibly() by adding it to it's

//invocation list.

del += new MulticastDelegate(MyClass.Multiply);

//Invoke Add() and Multiply() methods using the delegate.

//Note that these methods must have a void return vlue

Console.WriteLine("****calling Add() and Multibly() Methods.****\n\n");

del(5, 5);

//removing the Add() method from the invocation list

del -= new MulticastDelegate(MyClass.Add);

Console.WriteLine("\n\n****Add() Method removed.****\n\n");

//this will invoke the Multibly() method only.

del(5, 5);

Events are members of the class that raises them. When some thing happens a class can raise an event,
which have a message that contain information about the event (event arguments) and send them out to
the rest of the application, other parts of the application can respond to the event by executing methods
called event handlers.

Event handler is a method that has the same signature as the event and this method is executed when the
event occurs.
To define an event you need first to define a delegate that contains the methods that will be called when
the event raised, and then you define the event based on that delegate.

PROGRAM 3 : Example (EVENTS):

public class MyClass


{
public delegate void MyDelegate(string message);
public event MyDelegate MyEvent;
}
Raising an events is a simple step. First you check the event against a null value to ensure that the caller
has registered with the event, and then you fire the event by specifying the event by name as well as any
required parameters as defined by the associated delegate.

Example

if (MyEvent != null)
MyEvent(message);

So far so good, in the previous section you saw how to define an event and the delegate associated with it
and how to raise this event.

Now you will see how the other parts of the application can respond to the event. To do this you just need
to register the event handlers.

when you want to register an event handler with an event you must follow this pattern:

MyClass myClass1 = new MyClass();


MyClass.MyDelegate del = new MyClass.MyDelegate(myClass1_MyEvent);
myClass1.MyEvent += del;

or you can do this in one line of code

myClass1.MyEvent += new MyClass.MyDelegate(myClass1_MyEvent);

//this is the event handler


//this method will be executed when the event raised.
static void myClass1_MyEvent(string message)
{
//do something to respond to the event.
}

Let's see a full example to demonstrate the concept:

namespace EventsInCSharp
{
public class MyClass
{
public delegate void MyDelegate(string message);
public event MyDelegate MyEvent;

//this method will be used to raise the event.


public void RaiseEvent(string message)
{
if (MyEvent != null)
MyEvent(message);
}
}
class Program
{
static void Main(string[] args)
{
MyClass myClass1 = new MyClass();
myClass1.MyEvent += new MyClass.MyDelegate(myClass1_MyEvent);

Console.WriteLine("Please enter a message\n");


string msg = Console.ReadLine();

//here is we raise the event.


myClass1.RaiseEvent(msg);
Console.Read();
}
//this method will be executed when the event raised.
static void myClass1_MyEvent(string message)
{
Console.WriteLine("Your Message is: {0}", message);
}
}
}

we are doing great, but what if you want to define your event and it's associated delegate to mirrors
Microsoft's recommended event pattern. To do so you must follow this patten:

public delegate void MyDelegate(object sender, MyEventArgs e);


public event MyDelegate MyEvent;

As you can see the first parameter of the delegate is a System.Object, while the second parameter is a type
deriving from System.EventArgs.

The System.Object parameter represents a reference to the object that sent the event(such as MyClass),
while the second parameter represents information regarding the event.

If you define a simple event that is not sending any custom information, you can pass an instance of
EventArgs directly.
let's see an example:

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
namespace MicrosoftEventPattern
{
public class MyClass
{
public delegate void MyDelegate(object sender, MyEventArgs e);
public event MyDelegate MyEvent;

public class MyEventArgs : EventArgs


{
public readonly string message;
public MyEventArgs(string message)
{
this.message = message;
}
}
//this method will be used to raise the event.
public void RaiseEvent(string msg)
{
if (MyEvent != null)
MyEvent(this, new MyClass.MyEventArgs(msg));
}
}
class Program
{
static void Main(string[] args)
{
MyClass myClass1 = new MyClass();
myClass1.MyEvent += new MyClass.MyDelegate(myClass1_MyEvent);

Console.WriteLine("Please enter a message\n");


string msg = Console.ReadLine();

//here is we raise the event.


myClass1.RaiseEvent(msg);
Console.Read();
}
static void myClass1_MyEvent(object sender, MyClass.MyEventArgs e)
{
if (sender is MyClass)
{
MyClass myClass = (MyClass)sender;
Console.WriteLine("Your Message is: {0}", e.message);
}
}
}
}

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

There is this thing in C# called a delegate, which is going to be crucial to build interactions between our
objects. What's a delegate, you ask? Good question. A delegate is a pointer to a method. What's that
mean? Just like you can pass variable by reference, you can pass a reference to a method. Let me give
you an example.

Let's say we have a class with two methods that have the same signature (same return type and same
parameter configuration).

public class MyObject


{
public int Add(int param1, int param2)
{
return param1 + param2;
}
public int Multiply(int param1, int param2)
{
return param1 * param2;
}
}

We can point to either of the methods in our class by using a delegate declared as follows:

public delegate int MyMethodDelegate(int param1, int param2);

Now, if we have another class and want to execute either of the methods in MyObject, we can do it
through the delegate as in the "Do()" method in the class below. As a matter of fact, we can pass any
method with the same signature (not JUST the methods in MyObject). Check out the
MySecondObject.Subtract() method.

public class MySecondObject

{
MyObject obj;
int a, b;

public MySecondObject()
{
a=4;
b=5;
obj = new MyObject();
}

public int Do(string pMethod)


{
MyMethodDelegate del = null;

switch(pMethod)
{
case"Add":
del = new MyMethodDelegate(obj.Add);
break;
case"Multiply":
del = new MyMethodDelegate(obj.Multiply);
break;
case "Subtract":
del = new MyMethodDelegate(this.Subtract);
break;
}

if(null == del) throw new Exception("Not a valid call");

return del(a,b);
}

public int Subtract(int param1, int param2)


{
return param1 - param2;
}

Hopefully this gives you a better idea of what delegates are and how they are implemented.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Delegates are the function to pointer. A delegate keeps the reference of function. Like as pointer in C/C+
+, same in C# we have delegates. Delegates in C# are reference types. They are type safe, managed
function pointers in C# that can be used to invoke a method that the delegate refers to. The signature of
the delegate should be the same as the signature of the method to which it refers.

The signature of a delegate type comprises of the following.

• The name of the delegate


• The arguments that the delegate would accept as parameters
• The return type of the delegate

Let us see the working of delegates with this simple example. Here in this example, I am using a
button click event. A message appears when the button is clicked.
Creation a button control can be seen below.

this.button1 = new System.Windows.Forms.Button();


this.SuspendLayout();
//
// button1
//
this.button1.Location = new System.Drawing.Point(93, 71);
this.button1.Name = "button1";
this.button1.Size = new System.Drawing.Size(75, 23);
this.button1.TabIndex = 0;
this.button1.Text = "button1";
this.button1.UseVisualStyleBackColor = true;

//Here we see that on clicking on button a Event Handler is calling named as button1_click
this.button1.Click += new System.EventHandler(this.button1_Click);

The form1.cs code is:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace AnApplicationOfDelagatesNEvent
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)


{
MessageBox.Show("Hi this is Button click event");
}

public void MyMessage( object sender , System.EventArgs e)


{
MessageBox.Show("Hi this from My Method");

}
}
}

Here on the button click event a message appears. Here I also write a method.

Here if user run the application and click on button then:

Figure 1.

Now I am calling MyMessage on the click of Button. Here we see that now if user click on button then
the defination of button1_click will not be called because here Event is calling from MyMessage.

this.button1.Click += new System.EventHandler(MyMessage);


Figure 2.

Delegates Tutorial

This tutorial demonstrates the delegate types. It shows how to map delegates to static and instance
methods, and how to combine them (multicast).

A delegate in C# is similar to a function pointer in C or C++. Using a delegate allows the programmer to
encapsulate a reference to a method inside a delegate object. The delegate object can then be passed to
code which can call the referenced method, without having to know at compile time which method will
be invoked. Unlike function pointers in C or C++, delegates are object-oriented, type-safe, and secure.

A delegate declaration defines a type that encapsulates a method with a particular set of arguments and
return type. For static methods, a delegate object encapsulates the method to be called. For instance
methods, a delegate object encapsulates both an instance and a method on the instance. If you have a
delegate object and an appropriate set of arguments, you can invoke the delegate with the arguments.

An interesting and useful property of a delegate is that it does not know or care about the class of the
object that it references. Any object will do; all that matters is that the method's argument types and return
type match the delegate's. This makes delegates perfectly suited for "anonymous" invocation.

Note Delegates run under the caller's security permissions, not the declarer's permissions.

This tutorial includes two examples:

• Example 1 shows how to declare, instantiate, and call a delegate.


• Example 2 shows how to combine two delegates.

In addition, it discusses the following topics:

• Delegates and Events


• Delegates vs. Interfaces

Example 1 [shows how to declare, instantiate, and call a delegate]


The following example illustrates declaring, instantiating, and using a delegate. The BookDB class
encapsulates a bookstore database that maintains a database of books. It exposes a method
ProcessPaperbackBooks, which finds all paperback books in the database and calls a delegate for each
one. The delegate type used is called ProcessBookDelegate. The Test class uses this class to print out the
titles and average price of the paperback books.

The use of delegates promotes good separation of functionality between the bookstore database and
the client code. The client code has no knowledge of how the books are stored or how the bookstore code
finds paperback books. The bookstore code has no knowledge of what processing is done on the
paperback books after it finds them.

// bookstore.cs
using System;
// A set of classes for handling a bookstore:
namespace Bookstore
{
using System.Collections;
// Describes a book in the book list:
public struct Book
{
public string Title; // Title of the book.
public string Author; / Author of the book.
public decimal Price; // Price of the book.
public bool Paperback; // Is it paperback?
public Book(string title, string author, decimal price, bool paperBack)
{
Title = title;
Author = author;
Price = price;
Paperback = paperBack;
}
}
// Declare a delegate type for processing a book:
public delegate void ProcessBookDelegate(Book book);
// Maintains a book database.
public class BookDB
{
// List of all books in the database:
ArrayList list = new ArrayList();
// Add a book to the database:
public void AddBook(string title, string author, decimal price, bool paperBack)
{
list.Add(new Book(title, author, price, paperBack));
}
// Call a passed-in delegate on each paperback book to process it:
public void ProcessPaperbackBooks(ProcessBookDelegate processBook)
{
foreach (Book b in list)
{
if (b.Paperback)
// Calling the delegate:
processBook(b);
}
}
}
}
// Using the Bookstore classes:
namespace BookTestClient
{
using Bookstore;
// Class to total and average prices of books:
class PriceTotaller
{
int countBooks = 0;
decimal priceBooks = 0.0m;
internal void AddBookToTotal(Book book)
{
countBooks += 1;
priceBooks += book.Price;
}
internal decimal AveragePrice()
{
return priceBooks / countBooks;
}
}
// Class to test the book database:
class Test
{
// Print the title of the book.
static void PrintTitle(Book b)
{
Console.WriteLine(" {0}", b.Title);
}
// Execution starts here.
static void Main()
{
BookDB bookDB = new BookDB();
// Initialize the database with some books:
AddBooks(bookDB);
// Print all the titles of paperbacks:
Console.WriteLine("Paperback Book Titles:");
// Create a new delegate object associated with the static
// method Test.PrintTitle:
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));
// Get the average price of a paperback by using
// a PriceTotaller object:
PriceTotaller totaller = new PriceTotaller();
// Create a new delegate object associated with the nonstatic
// method AddBookToTotal on the object totaller:
bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));
Console.WriteLine("Average Paperback Book Price: ${0:#.##}",
totaller.AveragePrice());
}
// Initialize the book database with some test books:
static void AddBooks(BookDB bookDB)
{
bookDB.AddBook("The C Programming Language", "Brian W. Kernighan and Dennis M. Ritchie",
19.95m, true);
bookDB.AddBook("The Unicode Standard 2.0", "The Unicode Consortium", 39.95m, true);
bookDB.AddBook("The MS-DOS Encyclopedia", "Ray Duncan", 129.95m, false);
bookDB.AddBook("Dogbert's Clues for the Clueless", "Scott Adams", 12.00m, true);
}
}
}

Output

Paperback Book Titles:


The C Programming Language
The Unicode Standard 2.0

Dogbert's Clues for the Clueless


Average Paperback Book Price: $23.97

Code Discussion

• Declaring a delegate

The following statement:

public delegate void ProcessBookDelegate(Book book);

declares a new delegate type. Each delegate type describes the number and types of the
arguments, and the type of the return value of methods that it can encapsulate. Whenever a new
set of argument types or return value type is needed, a new delegate type must be declared.

• Instantiating a delegate: Once a delegate type has been declared, a delegate object must be
created and associated with a particular method. Like all other objects, a new delegate object is
created with a new expression. When creating a delegate, however, the argument passed to the
new expression is special - it is written like a method call, but without the arguments to the
method.

The following statement:

bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(PrintTitle));

creates a new delegate object associated with the static method Test.PrintTitle. The following
statement:

bookDB.ProcessPaperbackBooks(new ProcessBookDelegate(totaller.AddBookToTotal));

creates a new delegate object associated with the nonstatic method AddBookToTotal on the
object totaller. In both cases, this new delegate object is immediately passed to the
ProcessPaperbackBooks method.

Note that once a delegate is created, the method it is associated with never changes - delegate
objects are immutable.

• Calling a delegate: Once a delegate object is created, the delegate object is typically passed to
other code that will call the delegate. A delegate object is called by using the name of the delegate
object, followed by the parenthesized arguments to be passed to the delegate. An example of a
delegate call is:

processBook(b);

A delegate can either be called synchronously, as in this example, or asynchronously by using


BeginInvoke and EndInvoke methods.

Example 2 [shows how to combine two delegates]

This example demonstrates composing delegates. A useful property of delegate objects is that they can be
composed using the "+" operator. A composed delegate calls the two delegates it was composed from.
Only delegates of the same type can be composed.

The "-" operator can be used to remove a component delegate from a composed delegate.

// compose.cs
using System;
delegate void MyDelegate(string s);
class MyClass
{
public static void Hello(string s)
{
Console.WriteLine(" Hello, {0}!", s);
}
public static void Goodbye(string s)
{
Console.WriteLine(" Goodbye, {0}!", s);
}
public static void Main()
{
MyDelegate a, b, c, d;
// Create the delegate object a that references
// the method Hello:
a = new MyDelegate(Hello);
// Create the delegate object b that references
// the method Goodbye:
b = new MyDelegate(Goodbye);
// The two delegates, a and b, are composed to form c:
c = a + b;
// Remove a from the composed delegate, leaving d,
// which calls only the method Goodbye:
d = c - a;
Console.WriteLine("Invoking delegate a:");
a("A");
Console.WriteLine("Invoking delegate b:");
b("B");
Console.WriteLine("Invoking delegate c:");
c("C");
Console.WriteLine("Invoking delegate d:");
d("D");
}
}

Output

Invoking delegate a:
Hello, A!
Invoking delegate b:
Goodbye, B!
Invoking delegate c:
Hello, C!
Goodbye, C!
Invoking delegate d:
Goodbye, D!

Delegates and Events

Delegates are ideally suited for use as events — notifications from one component to "listeners" about
changes in that component.

Delegates vs. Interfaces

Delegates and interfaces are similar in that they enable the separation of specification and
implementation. Multiple independent authors can produce implementations that are compatible with an
interface specification. Similarly, a delegate specifies the signature of a method, and authors can write
methods that are compatible with the delegate specification. When should you use interfaces, and when
should you use delegates?

Delegates are useful when:

• A single method is being called.


• A class may want to have multiple implementations of the method specification.
• It is desirable to allow using a static method to implement the specification.
• An event-like design pattern is desired (for more information, see the Events Tutorial).
• The caller has no need to know or obtain the object that the method is defined on.
• The provider of the implementation wants to "hand out" the implementation of the specification
to only a few select components.
• Easy composition is desired.
Interfaces are useful when:

• The specification defines a set of related methods that will be called.


• A class typically implements the specification only once.
• The caller of the interface wants to cast to or from the interface type to obtain other interfaces or
classes.

Indexers In C#
By Rajesh

C# introduces a new concept known as Indexers which are used for treating an object as an array. The
indexers are usually known as smart arrays in C# community. Defining a C# indexer is much like
defining properties. We can say that an indexer is a member that enables an object to be indexed in the
same way as an array.

<modifier> <return type> this [argument list]


{
get
{
// Get codes goes here
}
set
{
// Set codes goes here
}
}
Where the modifier can be private, public, protected or internal. The return type can be any valid C#
types. The 'this' is a special keyword in C# to indicate the object of the current class. The formal-
argument-list specifies the parameters of the indexer. The formal parameter list of an indexer corresponds
to that of a method, except that at least one parameter must be specified, and that the ref and out
parameter modifiers are not permitted. Remember that indexers in C# must have at least one parameter.
Other wise the compiler will generate a compilation error.

The following program shows a C# indexer in action

// C#: INDEXER
// Author: rajeshvs@msn.com

using System;
using System.Collections;

class MyClass
{
private string []data = new string[5];
public string this [int index]
{
get
{
return data[index];
}
set
{
data[index] = value;
}
}
}

class MyClient
{
public static void Main()
{
MyClass mc = new MyClass();
mc[0] = "Rajesh";
mc[1] = "A3-126";
mc[2] = "Snehadara";
mc[3] = "Irla";
mc[4] = "Mumbai";
Console.WriteLine("{0},{1},{2},{3},{4}",mc[0],mc[1],mc[2],mc[3],mc[4]);
}
}
The indexers in C# can be overloaded just like member functions. The formal parameter list of an indexer
defines the signature of the indexer. Specifically, the signature of an indexer consists of the number and
types of its formal parameters. The element type is not part of an indexer's signature, nor is the names of
the formal parameters. The signature of an indexer must differ from the signatures of all other indexers
declared in the same class. C# do not have the concept of static indexers. If we declare an indexer static,
the compiler will show a compilation time error.

Indexers & Inheritance

Just like any other class members, indexers can also participate in inheritance. A base class indexer is
inherited to the derived class.

//C#: Indexer : Inheritance


//Author: rajeshvs@msn.com
using System;
class Base
{
public int this[int indxer]
{
get
{
Console.Write("Base GET");
return 10;
}
set
{
Console.Write("Base SET");
}
}
}
class Derived : Base
{

}
class MyClient
{
public static void Main()
{
Derived d1 = new Derived();
d1[0] = 10;
Console.WriteLine(d1[0]);//Displays 'Base SET Base GET 10'
}
}

Indexers & Polymorphism

A Base class indexer can be polymorphicaly overridden in a Derived class. But remember that the
modifiers like virtual, override etc are using at property level, not at accessor level.

//C#: Indexer : Polymorphism


//Author: rajeshvs@msn.com

using System;

class Base
{
public virtual int this[int index]
{
get
{
Console.Write("Base GET");
return 10;
}
set
{
Console.Write("Base SET");
}
}
}

class Derived : Base


{
public override int this[int index]
{
get
{
Console.Write("Derived GET");
return 10;
}
set
{
Console.Write("Derived SET");
}
}
}

class MyClient
{
public static void Main()
{
Base b1 = new Derived();
b1[0]= 10;
Console.WriteLine(b1[0]);//Displays 'Derived SET Derived GET 10'
}
}

Abstract Indexers

An indexer inside a class can be declared as abstract by using the keyword abstract. Remember that an
abstract indexer in a class carries no code at all. The get/set accessors are simply represented with a
semicolon. In the derived class we must implement both set and get assessors.

If the abstract class contains only set accessor, we can implement only set in the derived class.

The following program shows an abstract indexer in action.

//C#: Indexer : Abstract


//Author: rajeshvs@msn.com

using System;

abstract class Abstract


{
public abstract int this[int index]
{
get;
set;
}
}

class Concrete : Abstract


{
public override int this[int index]
{
get
{
Console.Write(" GET");
return 10;
}
set
{
Console.Write(" SET");
}
}
}

class MyClient
{
public static void Main()
{
Concrete c1 = new Concrete();
c1[0] = 10;
Console.WriteLine(c1[0]);//Displays 'SET GET 10'
}
}
Indexers & Properties

1. An index is identified by it's signature. But a property is identified it's name.


2. An indexer is always an instance member, but a property can be static also.
3. An indexer is accessed through an element access. But a property is through a member access.

Conclusion

The indexer is one of the key concepts of C# and are very interesting aspects of the language. I've given
you enough information to use indexer in your code and shown you some examples. The feedback is
always welcome. Feel free to contact me for any questions or comments you may have about this article
at rajeshvs@msn.com.

Comparison Between Properties and Indexers


Indexers are similar to properties. Except for the differences shown in the following table, all of the rules
defined for property accessors apply to indexer accessors as well.

Property Indexer
Identified by its name. Identified by its signature.
Accessed through a simple name or a member Accessed through an element access.
access.
Can be a static or an instance member. Must be an instance member.
A get accessor of a property has no A get accessor of an indexer has the same formal
parameters. parameter list as the indexer.
A set accessor of a property contains the A set accessor of an indexer has the same formal parameter
implicit value parameter. list as the indexer, in addition to the value parameter.

Properties
Properties are named members of classes, structs, and interfaces. They provide a flexible mechanism to
read, write, or compute the values of private fields through accessors.
Property Declaration

Properties are an extension of fields and are accessed using the same syntax. Unlike fields, properties do
not designate storage locations. Instead, properties have accessors that read, write, or compute their
values.

Property declaration takes one of the following forms:

[attributes] [modifiers] type identifier {accessor-declaration}


[attributes] [modifiers] type interface-type.identifier {accessor-declaration}

where:

attributes (Optional)

Additional declarative information. For more information on attributes and attribute classes, see
C# Attributes.

modifiers (Optional)

The allowed modifiers are new, static, virtual, abstract, override, and a valid combination of the
four access modifiers.

type

The property type, which must be at least as accessible as the property itself. For more
information on accessibility levels, see Access Modifiers.

identifier

The property name. For more information on interface member implementation, see interface.

accessor-declaration

Declaration of the property accessors, which are used to read and write the property.

interface-type

The interface in a fully qualified property name. See Interface Properties.

Remarks

Unlike fields, properties are not classified as variables. Therefore, it is not possible to pass a property as a
ref or out parameter.

A property declared using the static modifier is classified as a static property; otherwise, it is classified as
an instance property. Like other static members, a static property is not associated with a specific instance
and cannot be referenced through an instance. Instead, it is associated with the type and can only be
referenced through the type name. For example, in the following statements:
Button okButton = new Button();
// Using an instance property:
string s = okButton.Caption;

the Caption property is associated with the instance okButton. If Caption is declared as a static property,
the class name (Button) must be used instead:

// Using a static property:


string s = Button.Caption;

An instance of a class can be accessed using this in the accessors of an instance property, but it is an error
to use this in the accessors of a static property.

It is an error to use a virtual, abstract, or override modifier on an accessor of a static property.

PROGRAM 4 : Example (PROPERTIES)

This example demonstrates instance, static, and read-only properties. It accepts the name of the employee
from the keyboard, increments numberOfEmployees by 1, and displays the Employee name and number.

// property.cs
// Properties
using System;
public class Employee
{
public static int numberOfEmployees;
private static int counter;
private string name;

// A read-write instance property:


public string Name
{
get
{
return name;
}
set
{
name = value;
}
}

// A read-only static property:


public static int Counter
{
get
{
return counter;
}
}
// Constructor:
public Employee()
{
// Calculate the employee's number:
counter = ++counter + numberOfEmployees;
}
}

public class MainClass


{
public static void Main()
{
Employee.numberOfEmployees = 100;
Employee e1 = new Employee();
e1.Name = "Claude Vige";
Console.WriteLine("Employee number: {0}", Employee.Counter);
Console.WriteLine("Employee name: {0}", e1.Name);
}
}

Output
Employee number: 101
Employee name: Claude Vige

Indexers
Indexers permit instances of a class or struct to be indexed in the same way as arrays. Indexers are similar
to properties except that their accessors take parameters.
Indexer Declaration

Indexers allow you to index a class or a struct instance in the same way as an array. To declare an
indexer, use the following:

[attributes] [modifiers] indexer-declarator {accessor-declarations}

The indexer-declarator takes one of the forms:

type this [formal-index-parameter-list]


type interface-type.this [formal-index-parameter-list]

The formal-index-parameter takes the form:

[attributes] type identifier

where:

attributes (Optional)
Additional declarative information. For more information on attributes and attribute classes, see
C# Attributes.

modifiers (Optional)

Allowed modifiers are new, virtual, sealed, override, abstract, extern, and a valid combination
of the four access modifiers. For details, see Access Modifiers.

indexer-declarator

Includes the type of the element introduced by the indexer, this, and the formal-index-parameter-
list. If the indexer is an explicit interface member implementation, the interface-type is included.

type

A type name.

interface-type

The interface name.

formal-index-parameter-list

Specifies the parameters of the indexer. The parameter includes optional attributes, the index
type, and the index identifier. At least one parameter must be specified. The parameter modifiers
out and ref are not allowed.

accessor-declarations

The indexer accessors, which specify the executable statements associated with reading and
writing indexer elements.

identifier

The parameter name.

get Accessor

The get accessor body of an indexer is similar to a method body. It returns the type of the indexer. The
get accessor uses the same formal-index-parameter-list as the indexer. For example:

get
{
return myArray[index];
}

set Accessor

The set accessor body of an indexer is similar to a method body. It uses the same formal-index-
parameter-list as the indexer, in addition to the value implicit parameter. For example:

set
{
myArray[index] = value;
}

Remarks

The type of an indexer and each of the types referenced in the formal-index-parameter-list must be at
least as accessible as the indexer itself. For more information on accessibility levels, see Access
Modifiers.

The signature of an indexer consists of the number and types of its formal parameters. It does not include
the indexer type or the names of the formal parameters.

If you declare more than one indexer in the same class, they must have different signatures.

An indexer value is not classified as a variable; therefore, it is not possible to pass an indexer value as a
ref or out parameter.

To provide the indexer with a name that other languages can use for the default indexed property, use a
name attribute in the declaration. For example:

[System.Runtime.CompilerServices.CSharp.IndexerName("MyItem")]
public int this [int index] // Indexer declaration
{
}

This indexer will have the name MyItem. Without providing the name attribute, the default name will be
Item.

Example

The following example shows how to declare a private array field, myArray, and an indexer. Using the
indexer allows direct access to the instance b[i]. The alternative to using the indexer is to declare the array
as a public member and access its members, myArray[i], directly.

// cs_keyword_indexers.cs
using System;
class IndexerClass
{
private int [] myArray = new int[100];
public int this [int index] // Indexer declaration
{
get
{
// Check the index limits.
if (index < 0 || index >= 100)
return 0;
else
return myArray[index];
}
set
{
if (!(index < 0 || index >= 100))
myArray[index] = value;
}
}
}

public class MainClass


{
public static void Main()
{
IndexerClass b = new IndexerClass();
// Call the indexer to initialize the elements #3 and #5.
b[3] = 256;
b[5] = 1024;
for (int i=0; i<=10; i++)
{
Console.WriteLine("Element #{0} = {1}", i, b[i]);
}
}
}

Output
Element #0 = 0
Element #1 = 0
Element #2 = 0
Element #3 = 256
Element #4 = 0
Element #5 = 1024
Element #6 = 0
Element #7 = 0
Element #8 = 0
Element #9 = 0
Element #10 = 0

Notice that when an indexer's access is evaluated (for example, in a Console.Write statement) the get
accessor is invoked. Therefore, if no get accessor exists, a compile-time error occurs.

Defining an indexer allows you to create classes that act like "virtual arrays." Instances of that class can
be accessed using the [] array access operator. Defining an indexer in C# is similar to defining operator []
in C++, but is considerably more flexible. For classes that encapsulate array- or collection-like
functionality, using an indexer allows the users of that class to use the array syntax to access the class.

For example, suppose you want to define a class that makes a file appear as an array of bytes. If the file
were very large, it would be impractical to read the entire file into memory, especially if you only wanted
to read or change a few bytes. By defining a FileByteArray class, you could make the file appear similar
to an array of bytes, but actually do file input and output when a byte was read or written.
In addition to the example below, an advanced topic on Creating an Indexed Property is discussed in this
tutorial.

PROGRAM 5 : Example (INDEXERS)

In this example, the class FileByteArray makes it possible to access a file as if it were a byte array. The
Reverse class reverses the bytes of the file. You can run this program to reverse the bytes of any text file
including the program source file itself. To change the reversed file back to normal, run the program on
the same file again.

// indexer.cs
// arguments: indexer.txt
using System;
using System.IO;

// Class to provide access to a large file


// as if it were a byte array.
public class FileByteArray
{
Stream stream; // Holds the underlying stream
// used to access the file.
// Create a new FileByteArray encapsulating a particular file.
public FileByteArray(string fileName)
{
stream = new FileStream(fileName, FileMode.Open);
}

// Close the stream. This should be the last thing done


// when you are finished.
public void Close()
{
stream.Close();
stream = null;
}

// Indexer to provide read/write access to the file.


public byte this[long index] // long is a 64-bit integer
{
// Read one byte at offset index and return it.
get
{
byte[] buffer = new byte[1];
stream.Seek(index, SeekOrigin.Begin);
stream.Read(buffer, 0, 1);
return buffer[0];
}
// Write one byte at offset index and return it.
set
{
byte[] buffer = new byte[1] {value};
stream.Seek(index, SeekOrigin.Begin);
stream.Write(buffer, 0, 1);
}
}

// Get the total length of the file.


public long Length
{
get
{
return stream.Seek(0, SeekOrigin.End);
}
}
}

// Demonstrate the FileByteArray class.


// Reverses the bytes in a file.
public class Reverse
{
public static void Main(String[] args)
{
// Check for arguments.
if (args.Length == 0)
{
Console.WriteLine("indexer <filename>");
return;
}

FileByteArray file = new FileByteArray(args[0]);


long len = file.Length;

// Swap bytes in the file to reverse it.


for (long i = 0; i < len / 2; ++i)
{
byte t;

// Note that indexing the "file" variable invokes the


// indexer on the FileByteStream class, which reads
// and writes the bytes in the file.
t = file[i];
file[i] = file[len - i - 1];
file[len - i - 1] = t;
}

file.Close();
}
}
Input: indexer.txt

To test the program you can use a text file with the following contents (this file is called Test.txt in the
Indexers Sample).

public class Hello1


{
public static void Main()
{
System.Console.WriteLine("Hello, World!");
}
}

To reverse the bytes of this file, compile the program and then use the command line:

indexer indexer.txt

To display the reversed file, enter the command:

Type indexer.txt

Sample Output
}
}
;)"!dlroW ,olleH"(eniLetirW.elosnoC.metsyS
{
)(niaM diov citats cilbup
{
1olleH ssalc cilbup

Code Discussion

• Since an indexer is accessed using the [] operator, it does not have a name. For indexer
declaration syntax, see Indexers.
• In the example above, the indexer is of type byte and takes a single index of type long (64-bit
integer). The Get accessor defines the code to read a byte from the file, while the Set accessor
defines the code to write a byte to the file. Inside the Set accessor, the predefined parameter value
has the value that is being assigned to the virtual array element.
• An indexer must have at least one parameter. Although it is comparatively rare, an indexer can
have more than one parameter in order to simulate a multidimensional "virtual array." Although
integral parameters are the most common, the indexer parameter can be of any type. For example,
the standard Dictionary class provides an indexer with a parameter of type Object.
• Although indexers are a powerful feature, it is important to use them only when the array-like
abstraction makes sense. Always carefully consider whether using regular method(s) would be
just as clear. For example, the following is a bad use of an indexer:
class Employee
{
// VERY BAD STYLE: using an indexer to access
// the salary of an employee.
public double this[int year]
{
get
{
// return employee's salary for a given year.
}
}
}

Although legal, an indexer with only a Get accessor is rarely good style. Strongly consider using
a method in this case.

• Indexers can be overloaded

Properties Tutorial

This tutorial shows how properties are an integral part of the C# programming language. It demonstrates
how properties are declared and used.

This tutorial includes two examples. The first example shows how to declare and use read/write
properties. The second example demonstrates abstract properties and shows how to override these
properties in subclasses.

Example 1

This sample shows a Person class that has two properties: Name (string) and Age (int). Both properties
are read/write.

// person.cs
using System;
class Person
{
private string myName ="N/A";
private int myAge = 0;

// Declare a Name property of type string:


public string Name
{
get
{
return myName;
}
set
{
myName = value;
}
}

// Declare an Age property of type int:


public int Age
{
get
{
return myAge;
}
set
{
myAge = value;
}
}

public override string ToString()


{
return "Name = " + Name + ", Age = " + Age;
}

public static void Main()


{
Console.WriteLine("Simple Properties");

// Create a new Person object:


Person person = new Person();

// Print out the name and the age associated with the person:
Console.WriteLine("Person details - {0}", person);

// Set some values on the person object:


person.Name = "Joe";
person.Age = 99;
Console.WriteLine("Person details - {0}", person);

// Increment the Age property:


person.Age += 1;
Console.WriteLine("Person details - {0}", person);
}
}

Output
Simple Properties
Person details - Name = N/A, Age = 0
Person details - Name = Joe, Age = 99
Person details - Name = Joe, Age = 100
Code Discussion

• Notice the way that the properties are declared, for example, consider the Name property:

public string Name


{
get
{
return myName;
}
set
{
myName = value;
}
}

The Set and Get methods of a property are contained inside the property declaration. You can
control whether a property is read/write, read-only, or write-only by controlling whether a Get or
Set method is included.

• Once the properties are declared, they can be used as if they were fields of the class. This allows
for a very natural syntax when both getting and setting the value of a property, as in the following
statements:

person.Name = "Joe";
person.Age = 99;

• Note that in a property Set method a special value variable is available. This variable contains the
value that the user specified, for example:

myName = value;

• Notice the clean syntax for incrementing the Age property on a Person object:

person.Age += 1;
person.SetAge(person.GetAge() + 1);

• The ToString method is overridden in this example:

public override string ToString()


{
return "Name = " + Name + ", Age = " + Age;
}

Notice that ToString is not explicitly used in the program. It is invoked by default by the
WriteLine calls.
Example 2

The following example shows how to define abstract properties. An abstract property declaration does not
provide an implementation of the property accessors. The example demonstrates how to override these
properties in subclasses.

This sample consists of three files. In the Properties Sample, these files are compiled into a single
compilation but in this tutorial, each file is compiled individually and its resulting assembly referenced by
the next compilation:

• abstractshape.cs: The Shape class that contains an abstract Area property.


• shapes.cs: The subclasses of the Shape class.
• shapetest.cs: A test program to display the areas of some Shape-derived objects.

To compile the example, use the command line:

csc abstractshape.cs shapes.cs shapetest.cs

This will create the executable file shapetest.exe.

File 1 - abstractshape.cs

This file declares the Shape class that contains the Area property of the type double

// abstractshape.cs
// compile with: /target:library
// csc /target:library abstractshape.cs
using System;

public abstract class Shape


{
private string myId;

public Shape(string s)
{
Id = s; // calling the set accessor of the Id property
}

public string Id
{
get
{
return myId;
}

set
{
myId = value;
}
}

// Area is a read-only property - only a get accessor is needed:


public abstract double Area
{
get;
}

public override string ToString()


{
return Id + " Area = " + string.Format("{0:F2}",Area);
}
}

Code Discussion

• Modifiers on the property are placed on the property declaration itself, for example:

public abstract double Area

• When declaring an abstract property (such as Area in this example), you simply indicate what
property accessors are available, but do not implement them. In this example, only a Get accessor
is available, so the property is read-only.

File 2 - shapes.cs

The following code shows three subclasses of Shape and how they override the Area property to provide
their own implementation.

// shapes.cs
// compile with: /target:library /reference:abstractshape.dll
public class Square : Shape
{
private int mySide;

public Square(int side, string id) : base(id)


{
mySide = side;
}

public override double Area


{
get
{
// Given the side, return the area of a square:
return mySide * mySide;
}
}
}
public class Circle : Shape
{
private int myRadius;

public Circle(int radius, string id) : base(id)


{
myRadius = radius;
}

public override double Area


{
get
{
// Given the radius, return the area of a circle:
return myRadius * myRadius * System.Math.PI;
}
}
}

public class Rectangle : Shape


{
private int myWidth;
private int myHeight;

public Rectangle(int width, int height, string id) : base(id)


{
myWidth = width;
myHeight = height;
}

public override double Area


{
get
{
// Given the width and height, return the area of a rectangle:
return myWidth * myHeight;
}
}
}
File 3 - shapetest.cs

The following code shows a test program that creates a number of Shape-derived objects and prints out
their areas.

// shapetest.cs

// compile with: /reference:abstractshape.dll;shapes.dll


public class TestClass
{
public static void Main()
{
Shape[] shapes =
{
new Square(5, "Square #1"),
new Circle(3, "Circle #1"),
new Rectangle( 4, 5, "Rectangle #1")
};

System.Console.WriteLine("Shapes Collection");
foreach(Shape s in shapes)
{
System.Console.WriteLine(s);
}

}
}

Output
Shapes Collection
Square #1 Area = 25.00
Circle #1 Area = 28.27
Rectangle #1 Area = 20.00

Example 6. Working with strings

namespace Programming_CSharp
{
using System;
public class StringTester
{
static void Main( )
{
// create some strings to work with
string s1 = "abcd";
string s2 = "ABCD";
string s3 = @"Liberty Associates, Inc.provides custom .NET development,on-site Training &Dev”;

int result; // hold the results of comparisons


// compare two strings, case sensitive
result = string.Compare(s1, s2);
Console.WriteLine("compare s1: {0}, s2: {1}, result: {2}\n",s1, s2, result);

// overloaded compare, takes boolean "ignore case"


//(true = ignore case)
result = string.Compare(s1,s2, true);
Console.WriteLine("compare insensitive\n");
Console.WriteLine("s4: {0}, s2: {1}, result: {2}\n",s1, s2, result);

// concatenation method
string s6 = string.Concat(s1,s2);
Console.WriteLine("s6 concatenated from s1 and s2: {0}", s6);
// use the overloaded operator
string s7 = s1 + s2;
Console.WriteLine("s7 concatenated from s1 + s2: {0}", s7);

// the string copy method


string s8 = string.Copy(s7);
Console.WriteLine("s8 copied from s7: {0}", s8);

// use the overloaded operator


string s9 = s8;
Console.WriteLine("s9 = s8: {0}", s9);

// three ways to compare.


Console.WriteLine("\nDoes s9.Equals(s8)?: {0}",s9.Equals(s8));
Console.WriteLine("Does Equals(s9,s8)?: {0}",string.Equals(s9,s8));
Console.WriteLine("Does s9==s8?: {0}", s9 == s8);

// Two useful properties: the index and the length


Console.WriteLine("\nString s9 is {0} characters long. ",s9.Length);
Console.WriteLine("The 5th character is {1}\n",s9.Length, s9[4]);

// test whether a string ends with a set of characters


Console.WriteLine("s3:{0}\nEnds with Training?: {1}\n",s3,s3.EndsWith("Training") );
Console.WriteLine("Ends with Dev?: {0}",s3.EndsWith("Dev"));

// return the index of the substring


Console.WriteLine("\nThe first occurrence of Training ");
Console.WriteLine ("in s3 is {0}\n",
s3.IndexOf("Training"));

// insert the word excellent before "training"


string s10 = s3.Insert(101,"excellent ");
Console.WriteLine("s10: {0}\n",s10);

// you can combine the two as follows:


string s11 = s3.Insert(s3.IndexOf("Training"),"excellent ");
Console.WriteLine("s11: {0}\n",s11);
}
}
}

Output
compare s1: abcd, s2: ABCD, result: -1
compare insensitive
s4: abcd, s2: ABCD, result: 0
s6 concatenated from s1 and s2: abcdABCD
s7 concatenated from s1 + s2: abcdABCD
s8 copied from s7: abcdABCD
s9 = s8: abcdABCD
Does s9.Equals(s8)?: True
Does Equals(s9,s8)?: True
Does s9==s8?: True
String s9 is 8 characters long.
The 5th character is A
s3:Liberty Associates, Inc.
provides custom .NET development,on-site Training and Dev
Ends with Training?: False
Ends with Dev?: True
The first occurrence of Training in s3 is 101
s10: Liberty Associates, Inc.provides custom .NET development, on-site excellent Training and Dev
s11: Liberty Associates, Inc.provides custom .NET development,on-site excellent Training and
Dev

Example 7 :. Using a simple interface


using System;
// declare the interface
interface IStorable
{
// no access modifiers, methods are public
// no implementation
void Read( );
void Write(object obj);
int Status { get; set; }
}
// create a class which implements the IStorable interface
public class Document : IStorable
{
public Document(string s)
{
Console.WriteLine("Creating document with: {0}", s);
}
// implement the Read method
public void Read( )
{
Console.WriteLine("Implementing the Read Method for IStorable");
}
// implement the Write method
public void Write(object o)
{
Console.WriteLine("Implementing the Write Method for IStorable");
}
// implement the property
public int Status
{
get
{
return status;
}
set
{
status = value;
}
}
// store the value for the property
private int status = 0;
}
// Take our interface out for a spin
public class Tester
{
static void Main( )
{
// access the methods in the Document object
Document doc = new Document("Test Document");
doc.Status = -1;
doc.Read( );
Console.WriteLine("Document Status: {0}", doc.Status);
}
}
Output:
Creating document with: Test Document
Implementing the Read Method for IStorable
Document Status: -1

Example 8 : EXCEPTION

namespace Programming_CSharp
{
using System;
public class Test
{
public static void Main( )
{
Test t = new Test( );
t.TestFunc( );
}
// try to divide two numbers
// handle possible exceptions
public void TestFunc( )
{
try
{
double a = 5;
double b = 0;
Console.WriteLine ("{0} / {1} = {2}",a, b, DoDivide(a,b));
}
// most derived exception type first
catch (System.DivideByZeroException)
{
Console.WriteLine("DivideByZeroException caught!");
}
catch (System.ArithmeticException)
{
Console.WriteLine("ArithmeticException caught!");
}
// generic exception type last
catch
{
Console.WriteLine("Unknown exception caught");
}
}
// do the division if legal
public double DoDivide(double a, double b)
{
if (b == 0)
throw new System.DivideByZeroException( );
if (a == 0)
throw new System.ArithmeticException( );
return a/b;
}
}
}
Output:
DivideByZeroException caught!

Example 9 : Creating a hand-drawn Windows Form

using System;
using System.Windows.Forms;
namespace ProgCSharp
{
public class HandDrawnClass : Form
{

// a label to display Hello World


private System.Windows.Forms.Label lblOutput;

// a cancel button
private System.Windows.Forms.Button btnCancel;

public HandDrawnClass( )
{
// create the objects
this.lblOutput =new System.Windows.Forms.Label ( );
this.btnCancel =new System.Windows.Forms.Button ( );

// set the form's title


this.Text = "Hello World";

// set up the output label


lblOutput.Location =new System.Drawing.Point (16, 24);
lblOutput.Text = "Hello World!";
lblOutput.Size =new System.Drawing.Size (216, 24);

// set up the cancel button


btnCancel.Location =new System.Drawing.Point (150,200);
btnCancel.Size =new System.Drawing.Size (112, 32);
btnCancel.Text = "&Cancel";

// set up the event handler


btnCancel.Click +=new System.EventHandler (this.btnCancel_Click);

// Add the controls and set the client area


this.AutoScaleBaseSize =new System.Drawing.Size (5, 13);
this.ClientSize =new System.Drawing.Size (300, 300);
this.Controls.Add (this.btnCancel);
this.Controls.Add (this.lblOutput);
}

// handle the cancel event


protected void btnCancel_Click (object sender, System.EventArgs e)
{
Application.Exit( );
}

// Run the app


public static void Main( )
{
Application.Run(new HandDrawnClass( ));
}
}
}

1. Operator Overloading

2. Indexer

3. Inheritance

4. Polymorphism

5. Properties

6. Delegates

7. Multicasting

8. Events

S-ar putea să vă placă și