Was this page helpful?
Your feedback about this content is important. Let us know what you think.
Additional feedback?
1500 characters remaining
Visual C# .NET 2003 Language Changes

Visual C# .NET 2003 Language Changes

Visual Studio .NET 2003
 

Prashant Sridharan
Microsoft Corporation

December 30, 2002

Applies to:
   Microsoft Visual Studio® C# 2003

Summary: In an effort to remain 100% compliant with the European Computer Manufacturer's Association (ECMA) C# specification, Microsoft Corporation introduced several changes to its implementation of the C# compiler. These changes have varying impact on existing code and it is important for users to examine their code to ensure that it adheres to the required and recommended means of using the C# programming language. (6 printed pages.)

Contents

Background
New C# Language Features
Implementation Changes
Conclusion

Background

In late 2001, the C# programming language was ratified by the ECMA as a standard (ECMA-334). In keeping with Microsoft's commitment to the standards process for C# and the common language interface (CLI), several minor changes were made to the Microsoft C# compiler to adhere to both the spirit and letter of the ECMA C# standard. In addition, Microsoft made additional minor changes to its C# implementation that, while adhering to the C# standard specification, rectifies the handful of compiler bugs and errors that C# programmers have encountered. Each of these changes could cause code written with the Visual C# .NET 2002 generation of compilers to require modifications prior to use in Visual C# .NET 2003.

New C# Language Features

Two new features were added to the C# language in Visual C# .NET 2003. First, the compiler now supports the #line hidden preprocessor directive. Used primarily by source code generators, the #line hidden directive informs the compiler to omit debugger information for all lines of code immediately following the #line hidden directive up to and including the next encounter of the #line directive, provided the next occurrence is not a #line hidden directive in its own right. In the following example, the compiler generates IL code in which the WriteLine statements do not contain debugging information. In so doing, programmers debugging applications are prevented from stepping into the "hidden" code and examining its contents:

public class Customer
{
   public static void Main()
   {
      MyClass c = new MyClass();

      c.ExecuteCommand();

      #line hidden
      Console.WriteLine("Display some text");
      Console.WriteLine("Display some text");
      Console.WriteLine("Display some text");
      #line

      c.ProcessCommand();

      c.Close();
   }
}

However, the #line hidden directive does not hide compiler errors. Indeed, the compiler still compiles the code into IL and the code still executes; it simply prevents debuggers from stepping into its contents.

The second new C# feature involves XML Comments and is added for ECMA standards compliance. C# now supports the addition of XML Comments within multi-line comments written using the "slash-star" notation (/* and */). The following XML Comments are now legal in the 2003 generation of C# compilers:

/**
<summary>This is a
     comment

</summary>
*/

Moreover, for the sake of completeness, but by no means recommended for actual usage, programmers can mix and match comment styles and still write valid XML Comment code. In doing so, the following comment declaration is now legal:

/**
<summary>This is a
     comment
*/
/// </summary>

Implementation Changes

The 2003 generation of the C# compiler also has slightly different behavior than the 2002 version of the compiler in a handful of situations. It is possible that in a few cases these changes could cause code to either not compile or behave altogether differently than otherwise intended.

Changes to the "Foreach" Statement

The foreach statement now dynamically checks for the presence of the IDisposable interface in the data structure over which it iterates. Previously, the compiler never dynamically checked for the IDisposable interface unless the type returned from the GetEnumerator function implemented the IEnumerator interface. However, if the type is statically known to implement IDisposable, then the compiler always called Dispose. In other words, if the iterator type implemented the enumerator design pattern, but did not specifically implement the IEnumerator interface, the compiler would not call the Dispose method unless the iterator type was statically known to implement the IDisposable interface.

Now, in checking for the IDisposable interface, regardless of whether or not the iterator type implements the IEnumerator, the compiler calls the Dispose method, if it is implemented. In the following example, the Dispose method is not called in the Visual C# .NET 2002 compiler, but it is called in the Visual C# .NET 2003 compiler:

abstract class Base
{
   public int Current { get; }
   public bool MoveNext();
}

class Derived: Base, IDisposable
{
   // implementation of both Base and IDisposable
}

class MyClass
{
   public Base GetEnumerator()
   {
      return new Derived();
   }
}

When the foreach statement uses iterates over a collection of objects, it executes the GetEnumerator method and receives an instance of the Derived type cast to a Base type as its iterator type. The Base type, of course, need not implement the IEnumerator interface in order for its Current and MoveNext methods to be called. In the old compiler, the Derived type's Dispose method is not called because it does not implement IEnumerator and because the class Base isn't statically known to implement IDisposable. In the new compiler, the Dispose method is called because the compiler checks for the presence of the IDisposable interface on all of the foreach statement's iterator types. Because the result of the GetEnumerator call is a Derived type cast to a Base type, and because the Derived type implements the IDisposable interface, the compiler's dynamic check for the presence of the IDisposable interface results in a call to the Dispose method.

Changes to Property Declarations

The ECMA C# standard explicitly forbids the creation of getter and setter functions for corresponding properties. Indeed, the C# compiler converts a property declaration into getter and setter functions so that languages that do not support properties can still access the data. As such, the following code is invalid since the compiler generates get_Prop and set_Prop methods that conflict with those that are declared by the user:

public class MyClass
{
   public int Prop
   {
      get
      {
      }

      set
      {
      }
   }

   // illegal function now
   public int get_Prop()
   {
   }

   // illegal function now
   public void set_Prop(int val)
   {
   }
}

Previously, the C# compiler allowed the creation of such functions, which was clearly a bug. The 2003 generation of the C# compiler rectifies this mistake.

As a corollary to this compiler correction, the C# compiler also no longer allows the explicit creation of property-generated getter and setter functions when the property is defined as the result of an interface implementation. In the following example, the 2003 version of the C# compiler no longer allows the explicit implementation of the IMyInterface.get_Prop and IMyInterface.set_Prop methods in the Derived class:

interface IMyInterface
{
   public int Prop { get; set; }
}

public class Derived : IMyInterface
{
   public int Prop
   {
      get
      {
      }

      set
      {
      }
   }

   // illegal
   public int IMyInterface.get_Prop()
   {
   }

   // illegal
   public void IMyInterface.set_Prop(int val)
   {
   }
}

Other Changes

The previous version of the C# compiler permitted non-compliant use of attributes. These have been rectified in the 2003 generation of the C# compiler for greater conformance to the ECMA specification. The first of these fixes the C# compiler so that it no longer allows named parameters in its argument list which are not declared as public in the attribute's class declaration. For example, if an AuthorAttribute class is created with a private field named authorName, the following statement is allowed in the previous version of the C# compiler, but now results in the C# 2003 compiler error:

[Author(authorName="microsoftuser")]
public class MyClass
{
}

Second, the ObsoleteAttribute can now be applied to operators so that programmers can deprecate overloaded operator functions. Finally, where the compiler used to generate an error for an unrecognized attribute location, it now generates only a warning, as required by the ECMA C# specification.

In addition, the C# compiler used to accept user-defined shift operator arguments (<< and >>) that were invalid according to the ECMA C# specification. For example, the shift operator could once be declared as follows with the type of the enclosing class declared as the second operand:

public class MyClass
{
   public static MyClass operator <<(int I, MyClass c) 
   {
   }
 
   public static void Main()
   {
   }
}

According to the specification, if the left shift operator is overloaded, the first argument in the binary operator's operand list must be the same as the enclosing type. Likewise, if the right shift operator is overloaded, the second argument in the binary operator's operand list must be of the enclosing type. The following code example illustrates the proper way in which the left shift operator should be declared:

public class MyClass
{
   public static MyClass operator <<( MyClass c, int i) 
   {
   }
 
   public static void Main()
   {
   }
}

Finally, several fixes were incorporated to correct flaws in the compiler, including:

  • Corrections to the definite assignment algorithm so that the compiler no longer reports errors for code that is correct according to the ECMA C# specification.
  • Enumerated types can now be converted to characters, as documented in the ECMA C# specification.
  • The internal virtual warning has been removed as internal virtual functions can no longer be overridden outside the assembly.

Conclusion

The C# compiler implements several features differently in order to gain better performance than the previous version. None of these improvements should affect the compilation or execution of code:

  • When iterating over a string's elements the foreach statement now uses the string's indexer rather than the enumerator pattern, resulting in greater performance.
  • The C# compiler now conforms more strictly to the ECMA C# specification's instructions for handling floating point and decimal math algorithms.
  • Several bugs were fixed for control flow optimizations.
Show:
© 2015 Microsoft