Tuesday, September 25, 2007

 

Delegates - Talk About Name Confusion!

Working with delegates in C# can be bit confusing. Unlike most of object-oriented programming that makes a clear distinction in terminology between definitions (classes) and instances (objects), delegates have no such distinction. What is meant by the word "delegate" depends on the context in which it is being used.

Consider MyClass1 below. The word "delegate" refers to three different things: (1) the data type MyDelegateType that defines a method signature; (2) the variable MyDelegateVariable of type MyDelegateType that contains the list of methods to be called; and (3) the instance myDelegateMethodPointer that points to a specific instance and method to be called.


public class MyClass1
{
delegate void MyDelegateType();
MyDelegateType MyDelegateVariable;

public MyClass1()
{
MyDelegateType myDelegateMethodPointer =
new MyDelegateType(MyHandlerMethod);

MyDelegateVariable += myDelegatePointer;
}

private void MyHandlerMethod()
{
}

}

 

To avoid confusion, I tend to use the terms "delegate definition" to describe MyDelegateType, "delegate list" to describe MyDelegateVariable, "handler" to describe myDelegateMethodPointer, and "handler method" to describe MyHandlerMethod.

Monday, September 24, 2007

 

Event Add and Remove Overrides

In a previous post entitled "Events vs Delegates" I mentioned that events use delegates. I also said that the event keyword is basically an access modifier that prevents code outside the class from accessing anything other than the += and -= operators on the delegate.

Another way of thinking about an event, however, that it is really a specially kind of property that has add and remove accessors instead of the typical get and set accessors. The += is mapped to the add accessor and the -= is mapped to the remove accessor. If the accessors are omitted, the compiler just creates defaults and uses a delegate to store attached handlers.

As an example, consider the code below in which MyClass1 and MyClass2 essentially do the same thing. The compiler automatically creates default add and remove accessors for MyClass1, whereas they are explicitily declared for MyClass2.



public class MyClass1
{
public event EventHandler MyEvent;
}

public class MyClass2
{
private EventHandler _myEvent;
public event EventHandler MyEvent
{
add
{
_myEvent += value;
}
remove
{
_myEvent -= value;
}
}
}


 
Note that MyClass2 creates a private delegate that is used by the accessors, so MyClass2 is equivalent to the defaults generated MyClass1. But, overriding the accessors provides a significant amount of power to the class that is exposing the events. For example, the overloaded accessors may be implemented to log when handlers are attached, store handlers in a structure other than a delegate, or even reject attaching a handler by throwing an exception if some state criteria is not met. The important thing is, however, that code using the overridden MyClass2.MyEvent attach to it with the += and -= just like it would attach to MyClass1.MyEvent.

Tuesday, September 18, 2007

 

int vs System.Int32

Many C# programmers may be a bit confused over the built in type aliases. What is the difference between int and System.Int32, or string and System.String? The answer is simple....nothing! A type alias is nothing more than a name that has been mapped directly to a system type for convienence. At compile time, the compiler simply substitutes System.Int32 for int.

Another question people often have is whether the size is or is not subject to change depending on platform. For example, if an int is a System.Int32 now, will it be a System.Int64 in the next version of .NET? The answer is no. The C# language specfication defines the mappings, so changing the size would literally require a change to the language itself. See ECMA-334 section 11.1.4 for the simple types.

Some people ask whether or not they can create their own type aliases. Well, the answer is both yes and no. The C# language does not provide a way to create global type aliases that can be accessed across all files -- there are no typedefs in the C# language! But, C# does allow you to define type aliases at the top of each source file via using statements. These type aliases, however, only exist within the context of that source file.


using myalias = MyNameSpace.MyClass;

Wednesday, September 12, 2007

 

Dispose() and Partial Classes

C#.NET 2.0 introduced the concept of partial classes. Basically, this feature allows the fields and methods of a single class to be distributed across multiple files. Although it is not necessarily good design to have a class that is so big that it needs to be distributed across multiple files, it does come in handy -- especially for forms.

If you create new form MyForm in Visual Studio 2005, then VS2005 creates a partial class with two C# source files: MyForm.cs and MyForm.Designer.cs. From the file names, Visual Studio obviously intends for the designer-generated code to be placed in the second file. One may even argue that the main reason for allowing partial classes is to better isolate the designer-generated code from the programmer's code.

But, wait! The Dispose() method is in the MyForm.Designer.cs file, and if you try to define a Dispose() method in MyForm.cs the compiler will complain and say you have duplicate method definitions. So, the big question becomes...

If you need to add code to the Dispose() method, is it safe to modify the Dispose() method in the designer-generated file? If you do modify Dispose() in MyForm.Designer.cs, will the designer overwrite your changes at some point in the future?

The answer is simple. It is safe to change the Dipose() method in MyForm.Designer.cs. If you look closely at the MyForm.Designer.cs file you will notice that the Dispose() method is outside of "the region", and that the Dispose() method does not actually ever have to be modified by the designer again.

For you own sanity, however, you may even choose to move Dispose() into MyForm.cs instead of MyForm.Designer.cs.

Monday, September 10, 2007

 

Events vs Delegates

Everyone that has used C# has undoubtably used both delegates and events. And, everyone probably realizes that events use delegates. But, I would be willing to bet that few people actually understand the subtle differences.

Consider the following the following class.


public class MyClass1
{
public EventHandler MyDelegate;
public event EventHandler MyEvent;
}

 
Upon examination, you should realize that the two declarations are basically the same except that the second uses the event keyword in its declaration. But, what does the event keyword really do? To understand why the event keyword is needed when declaring MyEvent, you need to understand the issues that exist with MyDelegate.

In the first declaration, MyDelegate is a public field of delegate type EventHandler. Since this is public, code using MyClass1 can add or remove handlers to the delegate using += and -=. But, that's not all! Code outside of the class can also call any of the delegate methods, reasign the delegate completely or even invoke the delegate itself. This is a big problem! In a publisher-subscriber pattern, this would allow one subscriber to discover and remove other subscribers, or even falsely trigger the delegate.

The event keyword is essentially an access modifier that is designed to prevent that security issue. When a delegate field is declared with the event keyword, code outside of the class can only access the += and -= method of that delegate; none of the other methods of that delegate can be called, and the delegate cannot be cleared or with =.


public class MyClass2

private MyClass1 _myClass1;

public MyClass2(MyClass1 myClass1)
{
_myClass1 = myClass1;

_myClass1.MyDelegate +=
new EventHandler(MyHandlerMethod);

_myClass1.MyEvent +=
new EventHandler(MyHandlerMethod);

Delegate[] invocationList;

// This line is valid
invocationList =
_myClass1.MyDelegate.GetInvocationList();

// This line is *** INVALID ***
invocationList =
_myClass1.MyEvent.GetInvocationList();
}

/// This method would be called
private void MyHandlerMethod(object sender, EventArgs e)
{
}
}


 

Trailing Commas

Some people may have "accidentally" stumbled across a feature of the C# programming language:

In a list of items separated by commas, the last item in that list may have a comma even though it is not followed by another item.

For example, both of the following enum declarations are perfectly valid; the compiler will not generate a syntax error when it reaches the comma after enum value Value2C.


public enum MyEnum1
{
Value1A,
Value2A,
Value3A
}

public enum MyEnum2
{
Value2A,
Value2B,
Value2C,
}


This is indeed a feature of the C# language, and is not bug in the compiler; the comma is truely optional and is modeled after the C99 and C++ standards. Allowing the optional, trailing comma not only makes it easier for developers to rearrange and comment-out items in the list, but also makes it easier to programmatically-generate C# source code.

The optional, trailing comma can be used in three places as indicated by sections 19.7, 21.1 and 24.2 of the ECMA-334 standard:

§19.7: "Like Standard C++, C# allows a trailing comma at the end of an array-initializer. This syntax provides flexibility in adding or deleting members from such a list, and simplifies machine generation of such lists."

§21.1: "C# allows a trailing comma in an enum-body, just like it allows one in an array-initializer (§19.6)"

§24.2: "For convenience, a trailing comma is allowed in a global-attribute-section and an attribute-section, just as one is allowed in an array-initializer (§19.6)."

 

Type Initializers, Static Fields and Static Constructors

You probably already know that there are two ways to initialize static variables in C#.NET: in the field declaration, and in the static constructor. You may even think that the following two classes are essentially the same, but they may not be!


/// Initialize via field declaration
class MyClass1
{
static object s_obj = new object();
}

// Initialize via the static constructor
class MyClass2
{
static object s_obj;

static MyClass2()
{
s_obj = new object();
}
}


First of all, let me point out one somewhat obvious point: Creating the instance of type object in the static constructor instead of in the field declaration is good practice because it allows you to insert a try...catch around the instantiation. That could be useful, for example, when loading a string, icon, etc. from a localized resource file; it would allow you to log any exceptions (missing string keys, etc.) that may have been introduced by the localization team. But, this post is about something much more subtle.

Both classes have a type initializer that does the same thing: creates an instance of type object() and assigns it to static variable s_obj. A type initializer is a Common Language Intrastructure (CLI) concept. According the CLI Specification (ECMA 335)


A static constructor, however, is part of the C# Language Specification -- not part of the CLI. According the the C# Language Specification (ECMA 334)


This implies that a C# class cannot be marked as relaxed if it has a static constructor. In C#, all class are unrelaxed by default. You can mark a class as relaxed via the BeforeFieldInit class attribute. You should only mark a class as relaxed if you would like to be able to call certain static or instance methods (those that do not access static fields) without caring whether or not the static type is fully initialized.

Ok, now to put it all together...

In C#.NET, if you declare a static constructor, then you are guaranteed that the static field declarations will be executed before the static constructor, and that the static constructor will be executed before a class is instantiated or any static fields or methods are used.

But, if you do not use a static constructor then initialization of the static fields depends upon whether the class does or does not use the BeforeFieldInit attribute. If the class does not use the attribute, then the static field declarations will be executed before an instance of the class is created or any static members are accessed. However, if BeforeFieldInit is true, then the initialization of the static fields could occur before or after static or instance methods are called -- or even not run at all if no static fields are accessed; you are only guaranteed that the static fields are initialized before any static field is used for the first time.

Wednesday, September 5, 2007

 

CLI, CLR, MSIL, JIT, .NET, C#, CLS and CTS

To most .NET programmers, there will be nothing new in this particular post. But, it only seems fitting that the initial post to this blog should be this simplified overview, just to make sure we're all on the same page.

The Common Language Infrastructure (CLI) is an international standard defined in ECMA-335. The CLI specifies the common mechanics for high-level, CLI-compliant programming languages. This standard is neither language specific nor platform specific.

The Common Language Runtime (CLR) is Microsoft's implementation of the CLI standard. The CLR executes a type of bytecode known as the Microsoft Intermediate Language (MSIL). At run-time, that MSIL bytecode is turned into native, executable code by the Just-In-Time compiler (JIT compiler). In theory, any CLI-compliant programming language is supported on any platform with the appropriate compiler and runtime.

The term ".NET" refers to a Microsoft product that includes the CLR, JIT compiler, source code compilers, and an entire framework of CLI-compliant components targetted for the Windows platform.

C# is a CLI-compliant programming language defined by the C# Language Specification (C#LS) known as ECMA-334. C#.NET is Microsoft's implementation of the C#LS for use with the Microsoft CLR, JIT compiler and framework components on Windows platforms. Since C#.NET is the only real implementation of the C# programming language (aside from the Mono open source project), the terms "C#" and "C#.NET" are often used interchangeably.

There are several .NET languages, including C#.NET and VB.NET. Even though both of those languages are CLI-compliant, the languages themselves have different capabilities. The Common Language Specification (CLS) is a Microsoft specification that defines the guaranteed subset of common functionality that is useable by all .NET programming languages. For example, the CLS defines the Common Type System (CTS), and the CTS does not include unsigned integers because the VB.NET programming language does not support unsigned integers.

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]