360 likes | 405 Views
This article covers the special features of C# programming language, including delegates, events, and attributes. Topics discussed include understanding delegates, events, designing events for classes, understanding attributes, and programmatically inspecting assembly attributes.
E N D
Special Features of C# : Delegates, Events and Attributes. by Rajendra Singh Chandrawat rchandra@cs.odu.edu
Topics to be covered Delegates, Events and Attributes: • Understanding Delegates • Understanding Events • DEMO1 : How to Design Events for your Classes. • DEMO2 : Stock Management Module • Understanding Attributes • DEMO3 : Creating and Using Attributes in your .NET Applications. • DEMO4 : Programmatically Inspecting Assembly Attributes.
C# Delegates: • One of the most interesting concepts in C# is Event-Driven approach. • Delegation model is a process in which a task is handed over to some other process so as to accomplish it. (Ex.) • Delegates are the special objects in C# which are pointers to methods. • Implemented as classes derived from the base class System.Delegate. • How delegates differ from classes?
Understanding Delegates : The syntax for defining delegates looks like this: delegate <return type> <delegate name>( <parameter list> ); Example: Delegate int SomeDelegate(string s, bool b); when you instantiate delegates, you pass in the function name to which this delegate will refer as its constructor parameter. only functions that have the same signature as the delegate, can be passed as a parameter. Ex: private int SomeFunction(string str, bool bln){...}
Understanding Delegates Contd.. pass this function to SomeDelegate's constructor : SomeDelegate sd = new SomeDelegate(SomeFunction); Now, sd refers to SomeFunctioni.e. SomeFunction is registered to sd. sd("somestring", true);
Understanding Events: What is an Event ? Answer Events are variables of type delegates. Events are nothing but change of state. (Updation, Modification) Assume that a software project was successfully installed in a customer's place. Later, the maintenance department was notified by the client for some enhancements. It’s an event which triggers the corresponding manager to take necessary actions, and he handles it by delegating the task to his subordinates. Events Client's requisition for modification. Event Handler Subordinates who are going to execute that requisition. A Button is a class, when you click on it, the click event fires.
DEMO 1How to design events for your classes Scenario- A class named Counter, A method named CountTo(int countTo, int reachableNum), raises an “event” named “NumberReached” whenever it reaches the reachableNum.
Declaring Events public event NumberReachedEventHandler NumberReached; or public event NumberReachedDelegate NumberReached; public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e); If you somewhere want to instantiate this delegate, the function passed in as constructor parameter should have the same signature as this delegate.
Providing User with Some Information we provide our data for the user in a class which is derived from EventArgs class. public class NumberReachedEventArgs : EventArgs { private int _reached; public NumberReachedEventArgs(int num) { this._reached = num; } public int ReachedNumber { get { return _reached; } } }
Counter Class namespace Events { public delegate void NumberReachedEventHandler(object sender, NumberReachedEventArgs e); /// <summary> /// Summary description for Counter. /// </summary> public class Counter { public event NumberReachedEventHandler NumberReached; public Counter() { // // TODO: Add constructor logic here // } public void CountTo(int countTo, int reachableNum) { if(countTo < reachableNum) throw new ArgumentException( "reachableNum should be less than countTo"); for(int ctr=0;ctr<=countTo;ctr++) { if(ctr == reachableNum) { NumberReachedEventArgs e = new NumberReachedEventArgs( reachableNum); OnNumberReached(e); return;//don't count any more } } } protected virtual void OnNumberReached(NumberReachedEventArgs e) { if(NumberReached != null) { NumberReached(this, e); //Raise the event } } }
Raising an Event • Raising an event is accomplished through calling our event (an instance of some delegate named NumberReachedEventHandler): NumberReached(this, e); This way, all registered functions will be invoked.
why do we indirectly call NumberReached(this, e) through OnNumberReached(NumberReachedEventArgs e) method? This method is “protected”, it means it's available for classes which are derived from this class (inheriting classes). This method is also “virtual”, this means that it could be overridden in a derived class.
Usefulness of Overriding • Suppose you are designing a class which inherits from Counter class. By overriding OnNumberReached method, you can do some additional work in your class before the event gets raised. An example: protected override void OnNumberReached(NumberReachedEventArgs e) { //Do additional work base.OnNumberReached(e); } • Note that if you don't call base.OnNumberReached(e), the event will never be raised! This might be useful when you are inheriting from some class and want to eliminate some of its events! An interesting trick, huh?
Event Keyword and += Operator • what happens if we don't use “event” keyword? declaring the event keyword prevents any of the delegate’s users from setting it to null. • Why is this important? Imagine that as a client I would add to the delegates invocation list a callback to one of my class’ functions. So would other clients. All is well and good. Now imagine that someone, instead of using the “+=”, is simply setting the delegate to a new callback by using “=”. This basically just throws the old delegate and its invocation list down the drain and creates a whole new delegate with a single item in its invocation list. All the other clients will not receive their callbacks when the time comes. It is this kind of situation that having the event keyword is aiming to solve. An event declaration adds a layer of protection on the delegate instance. This protection prevents clients of the delegate from resetting the delegate and its invocation list, and only allows adding or removing targets from the invocation list
Multicast Delegates: • Multicast Delegate • Any manager in an organization will have one or more subordinates working under him. When a task is to be accomplished, the chief will not call each and every subordinate individually. Because, the chief does not know the specialization of all the subordinates. The chief will instruct or assign a task to the manager and the manager in turn will delegate the work to his subordinates. Obviously, any number of subordinates could be added or removed from a particular team at any point of time. • When a delegate is wrapped with more than one method, that is known as a multicast delegate. As stated above, a manager who points to more than one member is a multicast delegate. If a multicast delegate is called, it will successively call each method in order. It is derived from System.MulticastDelegate that in turn is derived from System.Delegate. System.MulticastDelegate has additional members to allow chaining of method calls together into a list.
Stock Management Module DEMO 2
Attributes • What are Attributes? • How to use existing attributes? • How to create your own attributes to use in your own projects?
Essentially, attributes are a means of decorating your code with various properties at compile time . Once associated with a program entity, the attribute can be queried at run time using a technique called Reflection. What are attributes?
How to use attributes? • In C# attributes are placed before the item that they will be "decorating" and they are put inside square braces []. • [SerializableAttribute()] public class MySerializableClass • SerializableAttribute [] sa = (SerializableAttribute []) type.GetCustomAttributes(typeof(SerializableAttribute), false)
DEMO 3 Creating and Using Attributes in Your .NET application
Creating our own Attributes : • Specifications: • The attribute we will create here will have an imaginary importance in our demo project. Let's say that it serves as a flag for our program to do something with the class it is applied to, the number signifying what method of testing to use. We also wish to have the class name itself, but this isn't required. • There will be two test classes, one with the default name, and the other specifying its name. • The driver class will search for our attribute on objects passed in. If the attribute exists it will printout the classname and call the attributes PrintOut method. If the attribute doesn't exist, it will print out a message saying that it doesn't exist.
TestAttribute.cs using System; namespace Test { public class TestAttribute : Attribute { public int TheNumber; public string Name; public TestAttribute(int number) { TheNumber = number; } public void PrintOut() { Console.WriteLine("\tTheNumber = {0}", TheNumber); Console.WriteLine("\tName = \"{0}\"", Name); } } }
TestClasses.cs using System; namespace Test { [Test(3)] public class TestClassA { public TestClassA() { } } [Test(4, Name = "TestClassB")] public class TestClassB { public TestClassB() { } } }
Driver.cs using System; namespace Test { public class Driver { public static void Main(string [] Args) { TestClassA a = new TestClassA(); TestClassB b = new TestClassB(); string c = ""; PrintTestAttributes(a); PrintTestAttributes(b); PrintTestAttributes(c); } public static void PrintTestAttributes(object obj) { Type type = obj.GetType(); TestAttribute [] AttributeArray = (TestAttribute []) type.GetCustomAttributes(typeof(TestAttribute), false); Console.WriteLine("Class:\t{0}", type.Name); if( AttributeArray.Length == 0 ) { Console.WriteLine("There are no TestAttributes applied to this class {0}", type.Name); return ; } TestAttribute ta = AttributeArray[0]; ta.PrintOut(); } } }
How it Works :- • All of the work is done by the framework with the call to GetCustomAttributes on the Type object. GetCustomAttributes takes two parameters, the first is a Type object for the attribute we wish to get, the second is a boolean telling the framework whether it should look at the types that this class derives from. GetCustomAttributes returns an object array containing each of the attributes found that match the Type passed in. In this case we requested all attributes of a specific type, so we can safely cast that to an array of our attribute. At the very least we could cast that to an array of Attribute's. If you wish to get all attributes attached to a type you just pass in value for the bool.
Contd… • Once it has gotten an array of TestAttribute's it determines how many are in the array; if there are none in the array we know that the class doesn't have the TestAttribute applied to it; else we take the first one in the array and tell it to output its values by the PrintOut method we created in the TestAttribute class.
“AttributeUsage” Attribute This attribute is used at compile time to ensure that attributes can only be used where the attribute author allows. This attribute also allows the author to specify whether the same attribute can be applied multiple times to the same object and if the attribute applies to objects that derive from the class it is applied to. A simple modification to TestAttribute.cs restricts our attribute to being applied only to classes. .... [AttributeUsage(AttributeTargets.Class)] public class TestAttribute : Attribute .... Now the TestAttribute can only be applied to classes. By default attributes aren't inherited and can only be applied once so we needn't do anything more with it. [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)] The usage above would allow the attribute to be applied to only classes and structs.
Summary • Attributes can be attached to virtually everything, classes, methods, fields, properties, parameters in methods, return values of methods.
DEMO 4 Programmatically Inspecting Assembly Attributes
Introduction • “Reflection” allows you to programmatically inspect an assembly, and get information about the assembly, including all object types contained within. • This information includes the attributes you have added to those types. • The reflection objects reside within the System.Reflection namespace.
Source Code Explanation The code first checks the parameters passed to the command-line - if none are supplied, or if the user types FindAttributes /? then the Usage() method will be called, which will display a simple command usage summary: if (args.Length == 0) Usage(); else if((args.Length == 1)&&(args[0] == "/?")) Usage();
Loading An Assembly We then attempt to load the assembly, and retrieve all custom attributes defined on that assembly with the GetCustomAttributes() method: Assembly a = Assembly.LoadFrom(assemblyName); // Now find the attributes on the assembly object[] attributes = a.GetCustomAttributes(true) ; // If there were any attributes defined... if(attributes.Length > 0) { Console.WriteLine("Assembly attributes for '{0}'...", assemblyName); // Dump them out... foreach(object o in attributes) Console.WriteLine(" {0}", o.ToString()); } Any attributes that are found are output to the console.
References: • http://www.codeproject.com/csharp/events.asp • http://www.codeproject.com/csharp/eventdrivenmodel.asp • http://www.codeproject.com/csharp/dotnetattributes.asp • http://www.codeproject.com/csharp/ayassemblyattributes.asp
Thanks !!! Questions/Suggestions ???