Login | Register   
RSS Feed
Download our iPhone app
Browse DevX
Sign up for e-mail newsletters from DevX


C#: Why Do We Need Another Language? : Page 3

New computer languages are rare and successful ones are rarer still, yet Microsoft decided to create a new language to go along with the .NET Developer Platform. Why weren't existing languages good enough?

Delegates and Events
The .NET Frameworks supports an event-based model for many operations. In this model, when a user presses a button on a form, a timer expires or a SQL connection goes down, user code can be executed.

To support such a model, there needs to be a way to hook up a function so that it can be called when an event occurs. In the C++ world, this would often be done with function pointers. In the .NET world, delegates are used to perform this function. Delegates are similar to function pointers in that they refer to a specific function, but have a few important advantages.

First, delegates are type-safe; they can only hook up to functions that have the proper signature. They also encapsulate not only the function to call but also the instance to use when making the call, which allows delegates to hook up to both static function and instance methods. Finally, delegates are multicast, which means that a single delegate can call multiple functions when invoked.

It's possible to build event-based systems using only delegates, by exposing them as public fields, but delegates by themselves don't provide any protection against user error. Instead of adding their delegate to an already-existing delegate, it's easy to replace the existing delegate.

To make the model more robust, events are layered on top of delegates. Events are somewhat like properties, in that they provide restricted access to an underlying field.

Here's an example of a class supporting an event:

using System; public class EventTest { public delegate void MyHandler(string s); public event MyHandler Test; public void TestEvent(string s) { if (Test != null) Test(s); } }

The delegate defines the signature of the function, and the TestEvent() method is used to fire it. The following class hooks up to the event:

public class Test { public static void Main() { EventTest et = new EventTest(); et.Test += new EventTest.MyHandler(Function); et.TestEvent("Hello"); } public static void Function(string s) { Console.WriteLine("Function: {0}", s); } }

This code creates an instance of the EventTest class, creates a delegate that points to Function and then attaches it to the event using the += operator.

When designing a complex system, you often need to pass declarative information to the runtime part of a system. A transactional system, for example, needs to know how a transaction should be applied to a specific object.

In most languages, this information must be part of the class definition. Typically, this is done by adding a function that returns the information to the class. The runtime component can then find this function and call it to get the information. This works, but has several disadvantages.

The first disadvantage is that the programmer has to clutter the class with a function that's merely there to return a static piece of information. A more important disadvantage is that there's no compile-time validation that the function is returning the proper information; if it returns a string rather than an integer, the error won't be found until the code is executed.

A final disadvantage is that this scheme only works well for information about a whole class. It's very difficult to pass information about a specific parameter on a method.

Attributes are the C# solution to this problem. An attribute is a piece of declarative information that's placed on a program element (including classes, methods, return values, events, etc.). When the program is compiled, the compiler validates that the attribute is correct in that usage and stores the attribute information in the metadata for that object. (See Sidebar: What Is Metadata) The runtime component can then use reflection to obtain the value of the attribute. The .NET serialization subsystem uses attributes to determine what it should do when serializing a class:

[Serializable] class Employee { string name; string address; [NotSerialized] ArrayList cachedPayroll; }

The Serializable attribute tells the runtime that it's okay to serialize this class while the NotSerialized attribute tells it that it shouldn't serialize the cachedPayroll field.

Attributes provide the designer with a very flexible way of specifying and obtaining information. The .NET Frameworks are heavy users of attributes for things like transactioning, marking a method as a Web service or specifying the details of interop or XML serialization.

Attributes are also extensible. Attributes are merely classes that inherit from the System.Attribute class. The use of an attribute is roughly analogous to calling the constructor of the class. To mark classes as secure, you could write the following class:

class SecureAttribute: System.Attribute { bool secure; public SecureAttribute(bool secure) { this.secure = secure } public bool Secure { get { return secure; } } }

Apply the attribute to a class:

[Secure(true)] class TaxRateClass { ... }

Then fetch it at runtime:

Type type = typeof(TaxRateClass); Attribute[] atts = type.GetCustomAttributes( typeof(SecureAttribute), true); foreach (SecureAttribute att in atts) { if (att.Secure) // ... handle true case here }

Comment and Contribute






(Maximum characters: 1200). You have 1200 characters left.



Thanks for your registration, follow us on our social networks to keep up-to-date