Sprachänderungen in Visual C# .NET 2003

Veröffentlicht: 07. Mai 2003 | Aktualisiert: 22. Jun 2004

Von Prashant Sridharan

Bei der Implementierung des C#-Compilers wurden einige Änderungen vorgenommen, um die vollständige Kompatibilität mit der C#-Spezifikation der European Computer Manufacturer's Association (ECMA) zu gewährleisten.

Auf dieser Seite

Hintergrundinformationen Hintergrundinformationen
Neue C#-Sprachfunktionen Neue C#-Sprachfunktionen
Implementierungsänderungen Implementierungsänderungen
Änderungen der "foreach"-Anweisung Änderungen der "foreach"-Anweisung
Änderungen der Eigenschaftendeklarationen Änderungen der Eigenschaftendeklarationen
Weitere Änderungen Weitere Änderungen
Schlussfolgerung Schlussfolgerung

Hintergrundinformationen

Ende 2001 wurde die C#-Programmiersprache von der ECMA als Standard (ECMA 334) ratifiziert. Aufgrund der Verpflichtung von Microsoft zur Einhaltung der Standardisierungsvorschriften für C# und das Common Language Interface (CLI) wurde der Microsoft C#-Compiler geringfügig geändert, um mit den Spezifikationen des ECMA-C#-Standards übereinzustimmen. Auch bei der C#-Implementierung hat Microsoft geringfügige Änderungen vorgenommen, durch die unter Berücksichtigung der C#-Standardspezifikation die wenigen Compilerbugs und die anderen Fehler behoben wurden, die C#-Programmierern zu schaffen machten. Aufgrund dieser Änderungen könnte es erforderlich werden, den mit Compilern der Visual C# .NET 2002-Generation geschriebenen Code zu ändern, bevor dieser in Visual C# .NET 2003 verwendet werden kann.

 

Neue C#-Sprachfunktionen

Die C#-Sprache in Visual C# .NET 2003 wurde um zwei neue Funktionen erweitert. Zum einen unterstützt der Compiler jetzt die #line hidden-Präprozessordirektive. Die in der Regel von Quellcodegeneratoren verwendete #line hidden-Direktive weist den Compiler an, Debuggerinformationen für alle Codezeilen wegzulassen, die unmittelbar auf die #line hidden-Direktive folgen. Dies gilt bis einschließlich der nächsten #line-Direktive, vorausgesetzt, es handelt sich dabei nicht um eine #line hidden-Direktive. Im folgenden Beispiel generiert der Compiler IL-Code, in dem die WriteLine-Anweisungen keine Debuginformationen enthalten. Dadurch wird verhindert, dass Debuganwendungen von Programmierern in den "ausgeblendeten" Bereich des Codes wechseln und dessen Inhalt überprüfen.

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(); 
   } 
}

Die #line hidden-Direktive blendet jedoch keine Compilerfehler aus. Der Compiler kompiliert den Code weiterhin in IL, und der Code wird auch weiterhin ausgeführt, doch wird verhindert, dass Debugger in den Inhalt wechseln.

Die zweite neue C#-Funktion betrifft XML-Kommentare und wurde hinzugefügt, um den ECMA-Standard zu erfüllen. C# unterstützt jetzt das Hinzufügen von XML-Kommentaren innerhalb mehrzeiliger Kommentare, die mit der "Schrägstrich-Sternchen"-Notation (/* und */) geschrieben wurden. Folgende XML-Kommentare sind jetzt in C#-Compilern der 2003-Generation zulässig:

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

Aus Gründen der Vollständigkeit, jedoch nicht zur tatsächlichen Verwendung empfohlen, können Programmierer Kommentarstile mischen und anpassen, wobei der so geschriebene XML-Kommentarcode weiterhin gültig ist. Die folgende Kommentardeklaration ist jetzt zulässig:

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

 

Implementierungsänderungen

In einigen Fällen reagiert die 2003-Generation des C#-Compilers geringfügig anders als die 2002-Version. In einigen wenigen Fällen kann es vorkommen, dass diese Änderungen dazu führen, dass der Code entweder nicht kompiliert wird oder aber völlig anders als vorgesehen reagiert.

 

Änderungen der "foreach"-Anweisung

Die foreach-Anweisung sucht jetzt dynamisch nach der IDisposable-Schnittstelle in der Datenstruktur, die sie iterativ durchläuft. In früheren Versionen führte der Compiler nur dann eine dynamische Suche nach der IDisposable-Schnittstelle durch, wenn der von der GetEnumerator-Funktion zurückgegebene Typ die IEnumerator-Schnittstelle implementierte. Wenn der Typ jedoch IDisposable statisch implementierte, rief der Compiler stets Dispose auf. Anders ausgedrückt: Wenn der Iterator-Typ zwar das Enumerator-Entwurfsmuster implementierte jedoch nicht speziell die IEnumerator-Schnittstelle, rief der Compiler nur dann die Dispose-Methode auf, wenn der Iterator-Typ die IDisposable-Schnittstelle statisch implementierte.

Bei der Suche nach der IDisposable-Schnittstelle ruft der Compiler jetzt bei der Implementierung stets die Dispose-Methode auf. Dabei ist es egal, ob der Iterator-Typ die IEnumerator-Schnittstelle implementiert oder nicht. Im folgenden Beispiel wird die Dispose-Methode nicht im Visual C# .NET 2002-Compiler, jedoch im Visual C# .NET 2003-Compiler aufgerufen:

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(); 
   } 
}

Wenn die foreach-Anweisung eine Auflistung von Objekten iterativ durchläuft, führt sie die GetEnumerator-Methode aus und empfängt eine Instanz des Derived-Typs, die in einen Base-Typ als dessen Iterator-Typ umgewandelt wurde. Der Base-Typ muss natürlich die IEnumerator-Schnittstelle nicht implementieren, damit Current- und MoveNext-Methoden aufgerufen werden. Im alten Compiler wird die Dispose-Methode des Derived-Typs nicht aufgerufen, da sie die IEnumerator-Schnittstelle nicht implementiert und da die Base-Klasse die IDisposable-Schnittstelle nicht statisch implementiert. Im neuen Compiler wird die Dispose-Methode aufgerufen, da der Compiler in allen Iterator-Typen der foreach-Anweisung nach der IDisposable-Schnittstelle sucht. Da der GetEnumerator-Aufruf zu einem in einen Base-Typ umgewandelten Derived-Typ führt, und der Derived-Typ die IDisposable-Schnittstelle implementiert, führt die dynamische Suche des Compilers nach der vorhandenen IDisposable-Schnittstelle zu einem Aufruf der Dispose-Methode.

 

Änderungen der Eigenschaftendeklarationen

Der ECMA-C#-Standard untersagt ausdrücklich das Erstellen von Getter- und Setter-Funktionen für entsprechende Eigenschaften. Der C#-Compiler konvertiert eine Eigenschaftendeklaration in Getter- und Setter-Funktionen, so dass Sprachen, die keine Eigenschaften unterstützen, weiterhin auf die Daten zugreifen können. Der folgende Code ist ungültig, da der Compiler get_Prop- und set_Prop-Methoden generiert, die mit den vom Benutzer deklarierten Methoden in Konflikt stehen:

public class MyClass 
{ 
   public int Prop 
   { 
   get 
   { 
   } 
   set 
   { 
   } 
   } 
   // illegal function now 
   public int get_Prop() 
   { 
   } 
   // illegal function now 
   public void set_Prop(int val) 
   { 
   } 
}

In früheren Versionen erlaubte der C#-Compiler das Erstellen solcher Funktionen, was einen klaren Fehler darstellte. Die 2003-Generation des C#-Compilers behebt diesen Fehler.

Als logische Folge dieser Korrektur erlaubt der C#-Compiler darüber hinaus nicht länger das explizite Erstellen von durch Eigenschaften generierten Getter- und Setter-Funktionen, wenn die Eigenschaft als Ergebnis einer Schnittstellenimplementierung definiert wird. Im folgenden Beispiel erlaubt die 2003-Version des C#-Compilers nicht länger die explizite Implementierung der IMyInterface.get_Prop- und IMyInterface.set_Prop-Methoden in der Derived-Klasse:

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) 
   { 
   } 
}

 

Weitere Änderungen

Die vorherige Version des C#-Compilers ließ die nicht konforme Verwendung von Attributen zu. Dies wurde in der 2003-Generation des C#-Compilers korrigiert, um eine größere Konformität mit der ECMA-Spezifikation zu erreichen. Erstens lässt der C#-Compiler jetzt in der Argumentliste benannte Parameter nur zu, wenn diese in der Klassendeklaration des Attributs als public deklariert sind. Wenn beispielsweise eine AuthorAttribute-Klasse mit einem privaten Feld namens authorName erstellt wird, ist die folgende Anweisung in der vorherigen Version des C#-Compilers zulässig, führt jedoch jetzt beim C# 2003-Compiler zu einem Fehler:

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

Zweitens kann jetzt das ObsoleteAttribute auf Operatoren angewendet werden, so dass Programmierer keine überladenen Operatoren mehr hinnehmen müssen. Während der Compiler früher bei einem nicht erkannten Attributpfad einen Fehler generierte, wird jetzt, wie in der ECMA-C#-Spezifikation gefordert, lediglich eine Warnung generiert.

Darüber hinaus akzeptierte der C#-Compiler in der vorherigen Version benutzerdefinierte Verschiebeoperatorargumente (<< und >>), die gemäß der ECMA-C#-Spezifikation ungültig waren. Der Verschiebeoperator konnte beispielsweise wie folgt deklariert werden, wobei der Typ der umschließenden Klasse als zweiter Operand deklariert wurde.

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

Wenn der linke Verschiebeoperator überladen ist, muss gemäß der Spezifikation das erste Argument in der Operandenliste des binären Operators mit dem umschließenden Typ übereinstimmen. Wenn der rechte Verschiebeoperator überladen ist, muss entsprechend das zweite Argument in der Operandenliste des binären Operators dem umschließenden Typ entsprechen. Das folgende Codebeispiel veranschaulicht, wie der linke Verschiebeoperator richtig zu deklarieren ist:

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

Schließlich wurden noch weitere Korrekturen vorgenommen, um Compilerfehler zu beheben:

  • Korrekturen am definiten Zuweisungsalgorithmus, so dass der Compiler nicht länger Fehler für Code meldet, der gemäß der ECMA-C#-Spezifikation korrekt ist.

  • Enumerierte Typen können jetzt in Zeichen konvertiert werden, wie in der ECMA-C#-Spezifikation dokumentiert.

  • Die interne virtuelle Warnung wurde entfernt, da interne virtuelle Funktionen nicht länger außerhalb der Assembly überschrieben werden können.

 

Schlussfolgerung

Der C#-Compiler implementiert mehrere Funktionen unterschiedlich, um die Leistung im Vergleich zur vorherigen Version zu verbessern. Keine dieser Verbesserungen sollte die Kompilierung oder Ausführung von Code beeinträchtigen.

  • Beim iterativen Durchlaufen von Zeichenfolgeelemente verwendet die foreach-Anweisung jetzt den Indexer der Zeichenfolge und nicht mehr das Enumeratormuster, was die Leistungsfähigkeit verbessert.

  • Der C#-Compiler entspricht jetzt genauer den Anweisungen der ECMA-C#-Spezifikation, was die Verarbeitung von mathematischen Algorithmen für Gleitkommata und Dezimalstellen angeht.

  • Mehrere Fehler wurden zur Optimierung der Ablaufsteuerung behoben.