oreilly.comSafari Books Online.Conferences.


AddThis Social Bookmark Button

Top Ten Traps in C# for C++ Programmers
Pages: 1, 2, 3

Trap #3: C# distinguishes between value types and reference types

Like C++, C# is a strongly typed language, and like C++, C# divides types into two sets: intrinsic (built-in) types offered by the language, and user-defined types that are defined by the programmer.

In addition to intrinsic types and user-defined types, C# differentiates between value types and reference types. Value types hold their value on the stack, like variables in C++, unless they are embedded within a reference type. Reference-type variables sit on the stack, but they hold the address of an object on the heap, much like pointers in C++. Value types are passed to methods by value (a copy is made) while reference types are effectively passed by reference.

Classes and interfaces create reference types, but note carefully that structs are value types, as are all the intrinsic types (see Trap #5).

Trap #4: Watch out for implicit boxing

Boxing and unboxing are the processes that enable value types (for example, integers) to be treated as reference types (objects). The value is "boxed" inside an object, and subsequently "unboxed" back to a value type. Every type in C#, including the intrinsic types, derive from Object and may be implicitly cast to an object. Boxing a value allocates an instance of Object and copies the value into the new object instance. Boxing is implicit, so when you provide a value type where a reference is expected the value is implicitly boxed. Boxing brings some performance overhead, so avoid boxing where possible, especially in large collections.

To return the boxed object back to a value type you must explicitly unbox it. The unboxing occurs in two steps: Check the object instance to make sure it is a boxed value of the given value type. Copy the value from the instance to the value-type variable. In order for the unboxing to succeed, the object being unboxed must be a reference to an object that was created by boxing a value of the value type.

	using System;
	public class UnboxingTest
	   public static void Main()
	      int i = 123;
	      object o = i;

	      // unboxing (must be explicit)
	      int j = (int) o;
	      Console.WriteLine(“j: {0}”, j);

If the object being unboxed is null or a reference to an object of a different type, an InvalidCastException is thrown.

Trap #5: Struct is very different in C#

In C++ a struct is nearly identical to a class. In C++, the only difference is that a struct has public access as its default (rather than private) and its inheritance is public by default (again, rather than private). Some C++ programmers use structs as data-only objects, but that is a convention not supported by the language and discouraged by many object-oriented designers.

In C#, a struct is a simple user-defined type, a lightweight alternative that is quite different from a class. While structs do support properties, methods, fields, and operators, structs don’t support inheritance or destructors.

More importantly, while a class is a reference type, a struct is a value type (see Trap #3). Thus, structs are useful for representing objects that do not require reference semantics. Structs are somewhat more efficient in their use of memory in arrays, however, they may be less efficient when used in collections. Collections expect references, and structs must be boxed (see Trap #4). There is overhead in boxing and unboxing, and classes may be more efficient in large collections.

Trap #6: Virtual methods must be explicitly overridden

In C#, the programmer’s decision to override a virtual method must be made explicit with the override keyword.

To see why this is useful, assume that a Window class was written by Company A, and that ListBox and RadioButton classes were written by programmers from Company B, using a purchased copy of the Company A Window class as a base. The programmers in Company B have little or no control over the design of the Window class, including future changes that Company A might choose to make.

Now suppose that one of the programmers for Company B decides to add a Sort method to ListBox:

	public class ListBox : Window
	   public virtual void Sort() {...}

This presents no problems until Company A, the author of Window, releases version 2 of its Window class. It turns out that the programmers in Company A also added a Sort method:

	public class Window
	   // ...
	   public virtual void Sort() {...}

In C++ the new virtual Sort method in Window would now act as a base method for the virtual Sort method in ListBox. The compiler would call the Sort method in ListBox when you intend to call the Sort in Window. In C#, a virtual function is always considered to be the root of a virtual dispatch, that is, once C# finds a virtual method, it looks no further up the inheritance hierarchy. If a new virtual SortWindow the run-time behavior of ListBox is unchanged. When ListBox is compiled again, however, the compiler generates a warning:

	"\class1.cs(54,24): warning CS0114: ‘ListBox.Sort()’ hides
	inherited member ‘Window.Sort()’.

To remove the warning, the programmer must indicate what he intends. He can mark the ListBox Sort method to new, to indicate that it is not an override of the virtual method in Window:

	public class ListBox : Window
	   public new virtual void Sort() {...}

This action removes the warning. If, on the other hand, the programmer does want to override the method in Window, he need only use the override keyword to make that intention explicit.

Pages: 1, 2, 3

Next Pagearrow