Click to Rate and Give Feedback
Related Articles
Here the author introduces SQL Server Data Services, which exposes its functionality over standard Web service interfaces.

By David Robinson (July 2008)
Here the author answers questions regarding the Entity Framework and provides an understanding of how and why it was developed.

By Elisa Flasko (July 2008)
Here we present techniques for programmatic and declarative data binding and display with Windows Presentation Foundation.

By Josh Smith (July 2008)
Systems that handle failure without losing data are elusive. Learn how to achieve systems that are both scalable and robust.

By Udi Dahan (July 2008)
More ...
Articles by this Author
Here Jeffrey Richter introduces his AsyncEnumerator class, which drives an iterator so that different thread pool threads can execute the same code at different times.

By Jeffrey Richter (June 2008)
Jeffrey Richter introduces his AsyncEnumerator class and explains how it harnesses some recent additions to the C# programming language that make working with the asynchronous programming model significantly easier.

By Jeffrey Richter (November 2007)
Jeff Richter uses the AsyncResult class to implement the CLR’s Asynchronous Programming Model to perform hardware device operations asynchronously.

By Jeffrey Richter (June 2007)


By Jeffrey Richter (March 2007)
SideShow Gadgets for Windows Vista are cool. Writing your own is even better. Find out how it's done.

By Jeffrey Richter (January 2007)


By Jeffrey Richter (November 2006)
What can a robot-programming toolkit do for you? Read on and find out.

By Jeffrey Richter (September 2006)
If multiple threads concurrently execute code that writes to or modifies a resource, then obviously the resource must be protected with a thread synchronization lock to ensure that the resource doesn't get corrupted.

By Jeffrey Richter (June 2006)
More ...
Popular Articles
Here the author introduces SQL Server Data Services, which exposes its functionality over standard Web service interfaces.

By David Robinson (July 2008)
OBA solution patterns help architects and developers build Office Business Applications (OBAs). This article introduces the seven core OBA solution patterns and applies one to a real-world problem.

By Steve Fox (March 2008)
With custom form regions in Outlook you can pull in data from designated data sources and truly customize your users' Outlook 2007 experience.

By Steve Fox (Launch 2008)
Systems that handle failure without losing data are elusive. Learn how to achieve systems that are both scalable and robust.

By Udi Dahan (July 2008)
More ...
Read the Blog
In the November issue of MSDN Magazine, Jeffrey Richter demonstrates some recent additions to the C# programming language that make working with the APM significantly easier. In the June ...
Read more!
The July 2008 issue of MSDN Magazine is now available online. Here's what's in the issue: Data Services: Develop ...
Read more!
The June 2008 issue features the first installment of a new MSDN Magazine column on software design fundamentals. We’ll discuss design patterns and principles in a manner that isn't bound to a specific tool or lifecycle methodology. In this issue, Jeremy Miller starts the Patterns in Practice column ...
Read more!
In the April 2008 issue of MSDN Magazine, Kenny Kerr introduced the Windows Imaging Component (WIC), showing you how you can use it to encode and decode different image ...
Read more!
A combination of the retained-mode graphics system and notification mechanisms such as dependency properties unleash the flexibility and power of Windows Presentation Foundation (WPF, allowing these objects to be targets of data bindings and animations. In the June 2008 issue of MSDN Magazine, Charles ...
Read more!
One problem with GUI programming in C++ is that most libraries are too low level, putting much of the burden on the programmer. In the June 2008 issue of MSDN Magazine, John Torjo introduces you eGUI++, a C++ library that gives you a ...
Read more!
More ...
This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.
MSDN Magazine
Special .NET Type Members
Jeffrey Richter
I
n the October and December 2000 .NET columns, I examined the fundamentals of types. This month I'll take a look at some of the special members that a type can define. These members encourage good object-oriented design while greatly simplifying the syntax required to manipulate a type and its object instances.

Type Constructors

      You are already familiar with constructors, which are responsible for setting an object instance to its initial state. In addition to instance constructors, the Microsoft® .NET common language runtime (CLR) also supports type constructors (also known as static constructors, class constructors, or type initializers). A type constructor can be applied to interfaces, classes, and value types. It allows the type to perform any initialization required before any members declared within the type are accessed. Type constructors accept no parameters and always have a return type of void. A type constructor only has access to a type's static fields and its usual purpose is to initialize those fields. A type's constructor is guaranteed to run before any instance of the type is created and before any static field or method of the type is referenced.
      Many languages (including C#) automatically generate type constructors for any types you define. However, some languages will require you to implement the type constructors explicitly.
      To understand type constructors, examine the following type (defined in C#):
class AType {
   static int x = 5;
}
When this code is built, the compiler automatically generates a type constructor for AType. This constructor is responsible for initializing the static field x to the value 5. If you're using ILDasm, you can easily spot type constructor methods because they have a name of .cctor (for class constructor).
      In C#, you can implement type constructors yourself by defining a static constructor method in your type. The use of the static keyword makes the constructor a type constructor rather than an instance constructor. Here is a very simple example:
class AType {
   static int x;

   static AType() {
      x = 5;
   }
}
This type definition is identical to the previous one. Note that a type constructor must never attempt to create any instances of its own type, and the constructor must not reference any of the type's nonstatic members.
      Finally, if you give the C# compiler the following code, it generates a single type constructor method.
class AType {
   static int x = 5;

   static AType() {
      x = 10;
   }
}
This constructor first initializes x to 5 and then initializes x to 10. In other words, the resulting type constructor generated by the compiler first contains the static field initialization code, which is then followed by the code in your type constructor method.

Properties

      Many types define attributes that can be retrieved or altered. Quite frequently, these attributes are implemented as field members of the type. For example, here is a type definition that contains two fields:
class Employee {
   public String Name;
   public Int32 Age;
}
      If you were to create an instance of this type, then you could easily get or set any of the following attributes with code like this:
Employee e = new Employee();
e.Name = "Jeffrey Richter"; // Set the Name attribute
e.Age = 36;                 // Set the Age attribute

Console.WriteLine(e.Name);  // Displays "Jeffrey Richter" 
      Working with attributes in this way is very common. However, in my opinion the previous code should never be implemented as shown. One of the covenants of object-oriented design and programming is data abstraction. Data abstraction means that your type's fields should never be publicly exposed because it's too easy to write code that improperly uses the fields, corrupting the state of the object. For example, it's all too easy for someone to corrupt an Employee object with code like this:
e.Age = -5; // How could someone be -5 years old?
      So, when designing a type, I strongly suggest that all your fields be private, or at least protectedâ€"never public. Then, to allow a user of your type to get or set an attribute, expose methods specifically for this purpose. Methods that wrap access to a field are typically called accessor methods. They can optionally perform sanity checking and ensure that the object's state is never corrupted. For example, I'd rewrite the Employee class shown previously to resemble the code in Figure 1. This is a simple example, but you can see the enormous benefit of abstracting the data fields. You can also see how easy it is to make properties read-only or write-only by just not implementing certain accessor methods.
      Abstracting the data as shown in Figure 1 has two disadvantages. First, there is more code to write since you now have to implement additional functions. Second, users of the type now must call methods rather than simply referring to a single field name:
e.SetAge(36);    // Updates the age
e.SetAge(-5);    // Throws an exception 
I think you'll all agree that these disadvantages are quite minor. Nevertheless, the runtime offers a mechanism called properties, which somewhat alleviates the first disadvantage and removes the second disadvantage entirely.
      The class shown in Figure 2 uses properties and is functionally identical to the class in Figure 1. As you can see, properties simplify the code slightly, but more importantly, they allow the caller to write their code as follows:
e.Age = 36;    // Updates the age
e.Age = -5;    // Throws an exception
      The return value from a get property accessor and the parameter passed to a set property accessor are of the same type. Set properties have a void return type and get properties have no parameters. Properties may be static, virtual, abstract, internal, private, protected, or public. In addition, properties may be defined in an interface, which I'll discuss later.
      I should also point out that properties do not have to be associated with a field. For example, the System.IO.FileStream type defines a Length property that returns the number of bytes in the stream. This length is not maintained in a field; instead, when the length property get method is called, it calls another function that asks the underlying operating system to return the number of bytes in the open file's stream.
      When you create a property, the compiler actually emits special get_PropName and/or set_PropName accessor methods (where PropName is the name of the property). Most compilers will understand these special methods and will allow developers to access the methods using the special property syntax. However, a compiler that complies with the Common Language Specification (CLS) is not required to fully support properties; the compiler is only required to support the calling of the special accessor methods.
      In addition, compilers that do fully support properties may require slightly different syntax for defining and using properties. For example, C++ with managed extensions requires the use of the __property keyword.

Index Properties

      Some types, like System.Collections.SortedList, expose a logical list of elements. To make accessing the elements in this type easy, the type can define an index property (also referred to as an indexer). An example of an index property is shown in Figure 3. Using this type's indexer is incredibly simple:
BitArray ba = new BitArray(14);
for (int x = 0; x < 14; x++) {
   // Turn all even numbered bits on
   ba[x] = (x % 2 == 0);
   Console.WriteLine("Bit " + x + " is " + (ba[x] ? "On" : "Off"));
}
      In the BitArray example in Figure 3, the indexer takes one Int32 parameter: bitPosition. While all indexers must have at least one parameter, they may have two or more parameters. These parameters (as well as the return type) may be of any type. It is quite common to create an indexer that takes a String as a parameter to look up values in an associative array. A type can offer multiple, overloaded indexers as long as their prototypes differ.
      Like set properties, a set indexer accessor method contains a hidden parameter, value, which indicates the new desired value when the accessor is called. The BitArray set accessor shows this value parameter being used.
      A well-designed indexer should have both get and set accessors. Even though you can implement only a get accessor (for read-only semantics) or only a set accessor (for write-only semantics), it is recommended that indexers always implement both accessors. The reason is simply that a user of an index wouldn't expect only half of the behavior. For example, the user would not expect to see a compiler error when writing the following two lines:
String s = SomeObj[5];  // Compiles OK if get accessor exists
SomeObj[5] = s;         // Compiler error if set accessor doesn't exist
      Indexers always act on a specific instance of a type and cannot be declared static. However, indexers may be marked as public, private, protected, or internal.
      When you create an index property, the compiler actually emits special get_Item and/or set_Item accessor methods. Most compilers will understand these special methods and will allow developers to access the methods using the special index property syntax. However, a CLS-compliant compiler is not required to fully support index properties; the compiler is only required to support the calling of the special accessor methods.
      Also, compilers that completely support index properties may require a somewhat different syntax for defining and using these properties. C++ with managed extensions, for example, requires you to use the __property keyword.

Conclusion

      The concepts discussed in this column are extremely important to all programmers developing for .NET. The special type members I mentioned make components first-class citizens in the common language runtime. That is, modern components are designed to support properties.
      In my next column, I'll introduce delegates and event members, as they are an integral part of component-based application development and design.
Jeffrey Richter (http://www.wintellect.com/) is the author of Programming Applications for Microsoft Windows (Microsoft Press, 1999), and is a co-founder of Wintellect (http://www.Wintellect.com), a software education, debugging, and consulting firm. He specializes in programming/design for .NET and Win32®. Jeff is currently writing a Microsoft .NET Framework programming book and offers .NET technology seminars.

From the February 2001 issue of MSDN Magazine

Page view tracker