Wichtige Änderungen in Visual C# 2008

Aktualisiert: Juli 2008

Wichtige Änderungen in Visual C# 2008 Service Pack 1

In der folgenden Tabelle sind alle wichtigen Änderungen in Visual C# 2008 Service Pack 1 aufgeführt, die für eine Anwendung bedeutsam sein können, die in der ursprünglichen Releaseversion von Visual C# 2008 oder in Visual C# 2005 erstellt wurde.

Änderungsnummer

Kategorie

Problem

Beschreibung

1

Überladungsauflösung

Typrückschluss ist nun für Arrays von Zeigertypen in der Auflösung von Methodenüberladungen enthalten.

In Visual C# 2008 und früheren Versionen werden Arrays von Zeigertypen aufgrund von Typrückschluss vom Prozess der Auflösung von Methodenüberladungen ausgeschlossen. Im folgenden Code wählt der Compiler von Visual C# 2005 die nicht generische Version von Test aus, da die generische Version von Test aufgrund ihres Typparameters int*[] nicht berücksichtigt wird. In Visual C# 2008 wird die generische Version von Test ausgewählt.

using System.Collections.Generic;
unsafe class Program
{
    static void Main()
    {
        IEnumerable<int*[]> y = null;
        Test(y); 
    }
// Selected by Visual C# 2008.
    static void Test<S>(IEnumerable<S> x) { } // Selected by Visual C# 2005.
    static void Test(object o) { } 
}

2

Indexer

Der Compiler erzeugt nun den Fehler "CS0466" nicht nur für Methoden, sondern auch für Indexer und Eigenschaften.

In der ursprünglichen Releaseversion von Visual C# 2008 und in früheren Versionen kann eine explizite Implementierung eines Indexers definiert werden, in der die Implementierung über einen params-Parameter verfügt, die Schnittstellendefinition jedoch nicht. Diese Konstruktion widerspricht der Spezifikation. In Visual C# 2008 SP1 erzeugt diese Konstruktion einen Compilerfehler CS0466, wie im folgenden Code gezeigt.

interface I
{
    int this[int[] p] { set; }
}
class Base : I
{
// Produces CS0466:
    int I.this[params int[] p]    {
        set
        {
        }
    }

}

3

Typen, die NULL-Werte zulassen, und ??-Ausdrücke

Der Compiler wertet Ausdrücke nun ordnungsgemäß aus, in denen Variablen, die NULL-Werte zulassen, mit sich selbst verglichen werden.

In der ursprünglichen Releaseversion von Visual C# 2008 wird der folgende Code kompiliert und gibt zur Laufzeit "false" aus. In Visual C# 2008 Service Pack 1 wird Compilerwarnung (Stufe 3) CS1718 erzeugt und "true" ausgegeben.

static class Program
{
    static void Main()
    {
        int? x = null;
        bool y = x == x;
        Console.WriteLine(y);
    }
}

4

try-finally in Iteratoren

Die Ausführung von geschachtelten finally-Blöcken von Iteratoren, die break-Anweisungen enthalten, wurde geändert.

In der ursprünglichen Releaseversion von Visual C# 2008 wird im folgenden Code der äußere finally-Block zweimal ausgeführt. In Visual C# 2008 SP1 wird der äußere finally-Block einmal ausgeführt.

using System;
using System.Collections;
using System.Collections.Generic;
public class Test
{
    public static void Main()
    {
        Console.WriteLine("in main");
        foreach (int i in GetInts())
        {
            Console.WriteLine("in foreach");
            break; 
        }
    }
    static IEnumerable<int> GetInts()
    {
        Console.WriteLine("in GetInts");
        while (true)
        {
            Console.WriteLine("in while");
            try
            {
                Console.WriteLine("in outer try");
                try
                {
                    Console.WriteLine("in inner try before yield");
                    yield return 1;
                    Console.WriteLine("in inner try after yield");
                    break;
                }
                finally
                {
                    Console.WriteLine("in inner finally");
                }
            }
            finally
            {
                Console.WriteLine("in outer finally");
            }
        }
    }
}

5

Ausdrucksbaumstrukturen

Falsches Boxing von Methodenausdrücken in Ausdrucksbaumstrukturen tritt nicht mehr auf.

In der ursprünglichen Releaseversion von Visual C# 2008 gibt der folgende Code "7,0" aus. Die Zeile Console.WriteLine(e.Compile()(default(T))); gibt null aus, da für S falsches Boxing ausgeführt wird. In Visual C# 2008 SP1 tritt kein Boxing auf, und das Programm gibt "7, 7" aus.

using System;
using System.Linq;
using System.Linq.Expressions;
class Program
{
    static void Main()
    {
        Test<S>();
    }
    static void Test<T>() where T : I
    {       
        Expression<Func<T, int>> e = x => x.SetX() + x.X;
// No boxing in SP1:
        Console.WriteLine(e.Compile()(default(T))); 
    }
}
interface I
{
    int X { get; }
    int SetX();
}
struct S : I
{
    public int X { get; private set; }
    public int SetX()
    {
        X = 7;
        return 0;
    }
}

6

Objektinitialisierer

Initialisierung von Werttypen in Objektinitialisierern wurde korrigiert.

In der ursprünglichen Releaseversion von Visual C# 2008 wird die lokale Variable b im folgenden Beispiel nicht ordnungsgemäß initialisiert, und dessen Member X hat den Wert 0. In Visual C# 2008 SP1 wird S.X in beiden new-Ausdrücken ordnungsgemäß mit 1 initialisiert.

using System;
using System.Linq;
using System.Linq.Expressions;
    class Program
    {
        static void Main()
        {
            Test<S>();
        }
        static void Test<T>() where T : I, new()
        {
            var a = new T();
            a.X = 1;
            Console.WriteLine(a.X);
            var b = new T { X = 1 };
            Console.WriteLine(b.X);
        }
    }
    interface I
    {
        int X { get; set; }
    }
    struct S : I
    {
        public int X { get; set; }
    }
// Original release version of Visual C# 2008 output: 1 0
// Visual C# 2008 SP1 output: 1 1

7

Typkonvertierung

NULL-Literale sind nicht mehr in Enumerationswerte konvertierbar.

In der ursprünglichen Releaseversion von Visual C# 2008 können NULL-Literale in einigen Fällen in Enumerationswerte konvertiert werden. In Visual C# 2008 SP1 werden bei einem solchen Versuch Compilerfehler CS1502 und Compilerfehler CS1503 ausgelöst. Dies ist in folgendem Beispiel dargestellt.

enum MyEnum
{
    Zero = 0,
    One = 1
}
class MyClass { }
class Program
{
    static void Main(string[] args)
    {
// Produces CS1502 and CS1503:
        Test((MyClass)null);         }
    static void Test(MyEnum x)
    {
        System.Console.WriteLine(x);
    }
}

8

Ausdrucksbaumstrukturen

Bei ungültigen Ausdrucksbaumstrukturen wird nun die richtige Ausnahme ausgelöst.

In der ursprünglichen Releaseversion von Visual C# 2008 wird bei einer Ausdrucksbaumstruktur, die einen Methodenaufruf einer Methode enthält, die nicht vom angegebenen Typ ist, eine System.Security.VerificationException ausgelöst. In Visual C# 2008 SP1 wird eine System.ArgumentException ausgelöst, wie im folgenden Code gezeigt.

using System;
using System.Reflection;
using System.Linq;
using System.Linq.Expressions;
class Program
{
    public struct S { }
    static void Main()
    {
        Type t = typeof(System.Enum);
        MethodInfo m = t.GetMethod("GetTypeCode");
        ParameterExpression p = Expression.Parameter(typeof(S), "s");
        Expression<Func<S, TypeCode>> e = Expression.Lambda<Func<S, TypeCode>>(
// Throws System.ArgumentException in Visual C# 2008 SP1:
            Expression.Call(p, m), p); 
        Func<S, TypeCode> f = e.Compile();
// Throws System.Security.VerificationException in the
// original release version of Visual C# 2008: 
        Console.WriteLine(f(new S())); 
    }
}

9

Attribute

"CharSet.Unicode" wird nun an Hilfstypen weitergegeben, die C# für Arrayfelder fester Größe erstellt.

Der C#-Compiler erstellt Hilfstypen, um Arrays fester Größe zu kapseln. In der ursprünglichen Releaseversion von Visual C# 2008 und früher ist das Layout von Arrays stets ANSI, auch wenn mit dem StructLayout-Attribut "CharSet.Unicode" festgelegt wird. Dies kann mit C#-Quellcode nicht geändert werden. In Visual C# 2008 SP1 wird der im StructLayout-Attribut festgelegte "CharSet"-Wert zur Konstruktion der Hilfsklasse verwendet. Dies wird im folgenden Code dargestellt.

using System;
using System.Runtime.InteropServices;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
unsafe struct Test
{
    public fixed char Chars[8];
}
class Program
{
    static void Main(string[] args)
    {
    }
}
Original release version of Visual C# 2008 MSIL:
.class sequential ansi sealed nested public beforefieldinit '<Chars>e__FixedBuffer0'
       extends [mscorlib]System.ValueType
{
  // ... 
} // end of class '<Chars>e__FixedBuffer0'
Visual C# 2008 SP1 MSIL:
.class sequential unicode sealed nested public beforefieldinit '<Chars>e__FixedBuffer0'
       extends [mscorlib]System.ValueType
{
  // . . . 
} // end of class '<Chars>e__FixedBuffer0'

10

Überlaufüberprüfung

stackalloc führt nun eine Überlaufüberprüfung aus.

In der ursprünglichen Releaseversion von Visual C# 2008 kann eine stackalloc-Speicherbelegung fehlschlagen, ohne dass eine Ausnahme ausgelöst wird. Der Grund dafür ist eine "unchecked mul"-Anweisung in der erstellten Microsoft Intermediate Language (MSIL) beim Multiplizieren der Länge des Arrays mit der Größe der einzelnen Elemente. In Visual C# 2008 SP1 wird eine "mul.ovf"-Anweisung anstelle einer "mul"-Anweisung generiert, sodass Überläufe eine System.OverflowEx ception auslösen, wenn zur Laufzeit der Versuch der Speicherbelegung unternommen wird.

class Program
{
    static void Main(string[] args)
    {
        int var = 0x40000000;
        unsafe
        {
            // 0x40000000 * sizeof(int) does not fit in an int.
            int* listS = stackalloc int[var]; 
// Visual C# 2008 SP1: System.OverflowException.
            listS[0] = 5; 
// Original release version of Visual C# 2008: 
// System.NullReferenceException.
        }
    }
}

11

Standardabfrageoperatoren

In Abfragen von nicht generischen Auflistungen wird nun C#-Standardumwandlungssemantik verwendet.

In LINQ-Abfrageausdrücken für nicht generische Auflistungen wie System.Collections.ArrayList wird die from-Klausel der Abfrage vom Compiler neu erstellt, sodass diese einen Aufruf des Cast<T>-Operators enthält. Cast<T> konvertiert alle Elementtypen in den in der from-Klausel der Abfrage angegebenen Typ. Außerdem führt der Cast<T>-Operator in der ursprünglichen Releaseversion von Visual C# 2008 einige Werttypkonvertierungen und benutzerdefinierte Konvertierungen aus. Diese Konvertierungen werden jedoch mit der System.Convert-Klasse und nicht mit der C#-Standardsemantik ausgeführt. Diese Konvertierungen verursachen in bestimmten Szenarien auch bedeutende Leistungsprobleme. In Visual C# 2008 SP1 wurde der Cast<T>-Operator geändert und löst eine InvalidCastException für Konvertierungen von numerischen Werttypen und benutzerdefinierte Konvertierungen aus. Mit dieser Änderung werden sowohl die nicht standardkonforme C#-Umwandlungssemantik als auch das Leistungsproblem entfernt. Diese Änderung ist im folgenden Beispiel dargestellt.

using System;
using System.Linq;
class Program
{
    public struct S { }
    static void Main()
    {
        var floats = new float[] { 2.7f, 3.1f, 4.5f };
        var ints = from int i in floats 
                   select i;
// Visual C# 2008 SP1 throws InvalidCastException. 
        foreach (var v in ints) 
            Console.Write("{0} ", v.ToString());
        // The original release version of Visual C# 2008
        // compiles and outputs 3 3 4
    }
}

Wichtige Änderungen in der ursprünglichen Releaseversion von Visual C# 2008

In der folgenden Tabelle sind alle wichtigen Änderungen in der ursprünglichen Releaseversion von Visual C# 2008 aufgeführt, die dazu führen können, dass eine in Visual C# 2005 erstellte Anwendung nicht kompiliert wird oder ihr Laufzeitverhalten ändert.

Änderungsnummer

Kategorie

Problem

Beschreibung

12

Typkonvertierung

Die Konvertierung eines konstanten Ausdrucks mit einem Wert von 0 (null) in eine Enumeration ist nun zulässig.

Ein Literal 0 ist implizit in einen beliebigen Enumerationstyp konvertierbar. In Visual C# 2005 und früheren Versionen des Compilers, können auch einige konstante Ausdrücke, die zu 0 ausgewertet werden, implizit in einen beliebigen Enumerationstyp umgewandelt werden. Die Regel, mit der festgelegt wird, welche dieser Ausdrücke umgewandelt werden können, ist jedoch unklar. In Visual C# 2008 können alle konstanten Ausdrücke, die gleich 0 sind, implizit in einen beliebigen Enumerationstyp umgewandelt werden.

Dies kann zu Änderungen im Verhalten von bestehendem Code führen, etwa bei der Auflösung von Methodenüberladungen, die darauf beruht, dass diese implizite Konvertierung nicht möglich ist. Der folgende Code kompiliert mit Visual C# 2005 und früheren Compilern erfolgreich. Dabei wird der Methodenaufruf für den short-Wert nur bis zur Überladung für int-Werte aufgelöst. In Visual C# 2008 ist dieser Aufruf mehrdeutig, da der short-Wert auch implizit nach E konvertierbar ist. In Visual C# 2008 ist dieses Verhalten geändert, sodass jeder konstante Ausdruck, der zu 0 ausgewertet wird, konvertiert werden kann.

public enum E
{
    Zero = 0,
    One = 1,
} 
class A
{
    public A(string s, object o)
    { System.Console.WriteLine("{0} => A(object)", s); } 
    public A(string s, E e)
    { System.Console.WriteLine("{0} => A(Enum E)", s); }
} 
class B
{
    static void Main()
    {
        A a1 = new A("0", 0);
        A a2 = new A("1", 1);
        A a3 = new A("(int) E.Zero", (int) E.Zero);
        A a4 = new A("(int) E.One", (int) E.One);
    }
}
Visual C# 2005 output:
0 => A(Enum E)
1 => A(object)
(int) E.Zero => A(object)
(int) E.One => A(object)
Visual C# 2008 output:
0 => A(Enum E)
1 => A(object)
(int) E.Zero => A(Enum E)
(int) E.One => A(object)

13

Attribute

Es tritt nun ein Fehler auf, wenn das gleiche "TypeForwardedTo"-Attribut in einer Assembly zweimal vorhanden ist.

In Visual C# 2005 wird kein Fehler verursacht, wenn eine Assembly zwei "System.Runtime.CompilerServices.TypeForwardedTo"-Attribute enthält, die denselben Typ als Ziel haben. In Visual C# 2008 wird Compilerfehler CS0739 generiert. Dies ist im folgenden Beispiel dargestellt.

// Class1.cs
// Causes CS0739:
    [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Test))]
    [assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(Test))] 
    public class Test
    {
        public static int Main()
        {
            Test f = new Test();
            return f.getValue();
        }
    }
    // Library1.cs
    public class Test
    {
        public int getValue()
        {
            return 0;
        }

}

14

Typfehler

Eine neue Warnung über die Verwendung eines Referenztypmembers in einer Struktur wurde hinzugefügt.

Die Regeln für die definitive Zuweisung für Strukturen erfordern entweder, dass die Struktur auf eine bestehende Instanz vom selben Typ festgelegt wird oder alle Member der Struktur zugewiesen werden, bevor auf sie verwiesen wird. In Visual C# 2005 wird keine Warnung und kein Fehler verursacht, wenn ein nicht zugewiesener Referenztypmember einer Struktur verwendet wird. In Visual C# 2008 wird Compilerwarnung (Stufe 1) CS1060 generiert. Dies ist im folgenden Beispiel dargestellt.

    public class U { public int i;}
    public struct T { public U u;}
    class Program
    {
        static void Main()
        {
            T t;
// Produces CS1060:    
            t.u.i = 0; 
        }
    }

15

Überlaufüberprüfung

Die Bereichsüberprüfung für const-Dezimaltypen wurde korrigiert.

Beim Umwandeln von const-Dezimaltypen in Visual C# 2005 ist die Bereichsüberprüfung nicht immer korrekt, und es können falsche Compilerfehler auftreten. Im folgenden Code wird in Visual C# 2008 der richtige Fehler erzeugt: Compilerfehler CS0031.

        static void Main()
        {
            const decimal d = -10m;
            unchecked
            {
                const byte b = (byte)d; //CS0031
            }
        }

16

Überlaufüberprüfung

Bei Konvertierungen nach long, die sich außerhalb des Bereichs befinden, wird nun der richtige Compilerfehler erzeugt.

Im folgenden Code wird in Visual C# 2005 kein Compilerfehler erzeugt. In Visual C# 2008 wird Compilerfehler CS0031 erzeugt.

class Conversion 
    {
        static void Main() 
        {
            long l2 = (long) 9223372036854775808M; //CS0031 
        }
    }

17

Puffer mit fester Größe

Wenn auf einen Puffer mit fester Größe in einer unsicheren Struktur zugegriffen wird, bevor dem Puffer ein Wert zugewiesen wurde, wird nun ein Compilerfehler erzeugt.

Die Regeln für die definitive Zuweisung für unsichere Zeiger erfordern, dass der Zeiger vor dem Dereferenzieren festgelegt wird. Beim Zugriff auf einen Zeiger auf ein Array in einer unsicheren Struktur vor dem Zuweisen eines Werts an den Zeiger wird in Visual C# 2005 kein Compilerfehler erzeugt. In Visual C# 2008 wird dabei Compilerfehler CS0165 erzeugt, wie im folgenden Code gezeigt.

    unsafe class Test
    {
        static void Main()
        {
            S* ps;
            ps->i[0]++;        } // CS0165
    }
    unsafe struct S
    {
        public fixed int i[10];
    }

18

Nebeneffekte werden in Ausdrücken, die den NULL-Sammeloperator enthalten, nun beibehalten.

Definitive Zuweisung und der ??-Operator.

In Visual C# 2005 werden in einigen Szenarien die Nebeneffekte auf der linken Seite eines Ausdrucks, der den NULL-Sammeloperator enthält, nicht beibehalten. Beispielsweise wird für die zweite Console.WriteLine-Anweisung im folgenden Beispiel ein falscher Compilerfehler mit der Meldung erzeugt, dass b nicht zugewiesen ist. In Visual C# 2008 kompiliert der gleiche Code ordnungsgemäß und ohne Fehler.

        static void Main()
        {
            int? a, b;
            a = null;
            Console.WriteLine((b = null) ?? 17);
// No error in Visual C# 2008:Console.WriteLine(a + b);  

}

19

try-finally in Iteratoren

Der finally-Block wird nun ausgeführt, wenn ein Iterator den try-Block mit continue oder goto verlässt.

In Visual C# 2005 wird in einer try-finally-Konstruktion beim Verlassen eines Iteratorblocks im try-Block mithilfe einer goto-Anweisung oder einer continue-Anweisung der finally-Block nicht ausgeführt. In Visual C# 2008 wird der finally-Block in solchen Fällen ausgeführt.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace DisposeTest
{
    class A : IDisposable
    {
        int m_n;
        internal A(int n)
        {
            m_n = n;
        }
        internal void Nothing() { }
        ~A()
        {
            Console.WriteLine("failed to dispose {0}", m_n);
        }
        #region IDisposable Members
        public void Dispose()
        {
            GC.SuppressFinalize(this);
            Console.WriteLine("dispose {0}", m_n);
        }
        #endregion
    }
    class Program
    {
        static IEnumerable<A> B()
        {
            for (int nCount = 0; nCount < 2; nCount++)
            {
                Console.WriteLine("loop start");
                using (A A = new A(nCount))
                {
                    Console.WriteLine("using start");
                    // Section 1.
                    // Dispose not called correctly in Visual C# 2005.
                    if ((nCount % 2) == 0)
                        continue;
                    // Section 2.
                    // Dispose not called correctly in Visual C# 2005.
                    yield return A;
                    Console.WriteLine("using end");
                }
                Console.WriteLine("loop end");
            }
            yield break;
        }
        static void Main(string[] args)
        {
            foreach (A A in B())
            {
                A.Nothing();
            }
            Console.ReadLine();
        }
    }

}

20

Basisklassen und Schnittstellen

Bei der Klassenkonstruktion werden nun explizite Implementierungen der gleichen Schnittstellenmember in Basisklassen ignoriert.

Wenn für eine Klasse in Visual C# 2005 keine Implementierung für einen Schnittstellenmember bereitgestellt wird, verwendet der Compiler auch dann Basisklassenimplementierungen, wenn diese als explizite Schnittstellenimplementierungen deklariert sind. Dieses Verhalten stimmt nicht mit der ECMA (European Computer Manufacturers Association)-Spezifikation überein. Visual C# 2008 implementiert die Spezifikation ordnungsgemäß. Im folgenden Beispiel wird in Visual C# 2005 "B.Test" ausgegeben. In Visual C# 2008 wird ordnungsgemäß "A.Test" ausgegeben. Die Test-Methode in Klasse "B" wird ignoriert, da es sich um eine explizite Schnittstellenimplementierung handelt.

using System;
interface ITest
{
    string Test { get; }
    string Test2 { get; }
}
class A : ITest
{
    public string Test { get { return "A.Test"; } }
    public string Test2 { get { return "A.Test2"; } }
}
class B : A, ITest
{
    string ITest.Test { get { return "B.Test"; } }
    string ITest.Test2 { get { return "B.Test2"; } }
}
class C : B, ITest
{
    string ITest.Test2 { get { return "C.Test2"; } }
}
class Program
{
    static void Main()
    {
        C c = new C();
        Console.WriteLine(c.Test); 
// Visual C# 2008: "A.Test"
    }

}

21

Attribute

Bei der Verwendung eines veralteten Members wird nun eine Compilerwarnung generiert.

Methoden können mit dem Obsolete-Attribut markiert werden, damit zur Kompilierzeit Fehler oder Warnungen erzeugt werden, wenn diese Methoden aufgerufen werden. Wenn dieses Attribut für virtuelle Methoden festgelegt werden soll, muss es für die Basismethode festgelegt werden. Wenn das Obsolete-Attribut für eine überschreibende Methode festgelegt wird, werden beim Aufruf keine Compilerfehler oder -warnungen erzeugt. In Visual C# 2005 lässt der Compiler das Festlegen des Obsolete-Attributs für eine überschreibende Methode zu, obwohl das Attribut in diesem Fall keine Wirkung hat. In Visual C# 2008 wird eine Compilerwarnung Compilerwarnung (Stufe 1) CS0809 ausgegeben: "Veraltetes Member 'A.Filename' überschreibt nicht veraltetes Member 'Error.Filename'." Im folgenden Beispiel wird diese Warnung verursacht:

class A : Error
{
    [System.ObsoleteAttribute("Obsolete", true)]
    public override string Filename
    {
        set
        {
        }
    }
    public static void Main() { }
}
public class Error
{
    public virtual string Filename
    {
        set
        {
        }
        get
        {
            return "aa";
        }
    }
}
class B
{
    void TT()
    {
        new A().Filename = "Filename";
    }
}

22

Buildfehler

Bei der Verwendung der /pdb-Compileroption ohne /debug wird nun ein Fehler erzeugt.

In Visual C# 2005 werden keine Warnungen oder Fehler angezeigt, wenn die /pdb-Option ohne die /debug-Option angegeben wird. In Visual C# wird nur ein Releasebuild erstellt. Die PDB-Datei wird nicht generiert. Wenn in der ursprünglichen Releaseversion von Visual C# 2008 die Option /pdb ohne die Option /debug festgelegt wird, zeigt der Compiler Compilerfehler CS2036 an.

23

Typfehler

Es wird nun ein Fehler erzeugt, wenn eine switch-Bedingung leer ist.

In Visual C# 2005 wird kein Fehler erzeugt, wenn in einer switch-Anweisung eine void-Methode aufgerufen wird. In Visual C# 2008 wird Compilerfehler CS0151 erzeugt.

class C
{
    static void Main()
    {
// Produces CS0151:
        switch (M()) 
        {
            default:
                break;
        }
    }
    static void M()
    {
    }

}

24

Überlaufüberprüfung

Bei der Umwandlung von konstanten Dezimaltypen in ganzzahlige Typen wird nun ein anderer Compilerfehler erzeugt.

In Visual C# 2005 wird für folgenden Code Compilerfehler CS0133 erzeugt: "Der Ausdruck, der 'b' zugewiesen wird, muss konstant sein."

const byte b = unchecked((byte)256M);

In Visual C# 2008 wird Compilerfehler CS0031 erzeugt: "Der Konstantenwert '256M' kann nicht in 'Byte' konvertiert werden". Beachten Sie, dass der Fehler auch dann erzeugt wird, wenn der unchecked-Modifizierer angewendet wird.

25

Konstante Ausdrücke

Die Spezifikation wird hinsichtlich konstanter Ausdrücke genauer befolgt.

In Visual C# 2008 wurden einige Probleme behoben, die dazu führten, dass in Visual C# 2005 fälschlicherweise Operatoren und Variablen in konstanten Ausdrücken zulässig sind. In Visual C# 2005 kompiliert der folgende Code ohne Fehler. In Visual C# 2008 werden Compilerfehler CS0165, Compilerwarnung (Stufe 1) CS0184 und Compilerwarnung (Stufe 3) CS1718 erzeugt:

class Program
{
    public static int Main()
    {
        int i1, i2, i3, i4, i5;
        // 'as' is not permitted in a constant expression.
        if (null as object == null)
            i1 = 1;
        // 'is' is not permitted in a constant expression.
        if (!(null is object))
            i2 = 1;
        // A variable is not permitted in a constant expression.
        int j3 = 0;
        if ((0 == j3 * 0) && (0 == 0 * j3))
            i3 = 1;
        int j4 = 0;
        if ((0 == (j4 & 0)) && (0 == (0 & j4)))
            i4 = 1;
        int? j5 = 1;
// Warning CS1718: Comparison made to same variable:
        if (j5 == j5) 
 
            i5 = 1;
        System.Console.WriteLine("{0}{1}{2}{3}{4}{5}", i1, i2, i3, i4, i5);
        return 1;
    }
}

26

Typfehler

Es wird nun ein Fehler erzeugt, wenn ein statischer Typ als Parameter in einem Delegat oder einem Lambda-Ausdruck verwendet wird.

In Visual C# 2005 wird kein Fehler erzeugt, wenn ein statischer Typ als Parameter für ein Delegat oder eine anonyme Methode verwendet wird. Statische Typen können nicht als Typen von Methodenparametern verwendet werden, da sie nicht instanziiert werden können. Die Visual C# 2005-Version des Compilers lässt statische Typen als Parametertypen in Delegaten und Deklarationen von anonymen Methoden zu. Wenn NULL als Parameter übergeben wird, können diese Delegaten aufgerufen werden. In Visual C# 2008 wird der Fehler Compilerfehler CS0721 erzeugt, wenn ein statischer Typ als Parameter für ein Delegat oder eine anonyme Methode verwendet wird. Dies ist in folgendem Beispiel dargestellt.

public static class Test { }
public class Gen<T> { }
// Produces CS0721:
delegate int D(Test f); 
public class TestB
{
    public static void Main()
    {
        D d = delegate(Test f) { return 1; };
    }

}

27

Typen, die NULL-Werte zulassen, und ??-Ausdrücke

Es wird keine Warnung erzeugt, wenn eine Konstante in einen Typ, der NULL-Werte zulässt, umgewandelt wird, bevor die Konstante einem umfassenderen Typ, der NULL-Werte zulässt, zugewiesen wird.

In Visual C# 2005 wird für folgenden Code Compilerwarnung (Stufe 3) CS0219 erzeugt. In Visual C# 2008 wird keine Warnung erzeugt.

ushort? usq2 = (byte?)0;

28

Überladungsauflösung

Es wird nun ein Fehler erzeugt, wenn für anonyme Methoden mehrdeutige Überladungsauflösungen auftreten.

Der Compiler muss Methodenaufrufe für überladene Methoden auflösen, um festzulegen, welche spezifische Überladung aufgerufen werden soll. Wenn der Parametertyp eines Aufrufs teilweise abgeleitet wird, kann die aufzurufende Überladung mehrdeutig werden. Dies verursacht einen Compilerfehler.

Im Falle einer anonymen Methode, die als Parameter eines Delegats übergeben wird, wird der Delegattyp der anonymen Methode teilweise abgeleitet. Dies kann beim Auswählen der richtigen Überladung durch den Compiler zu Mehrdeutigkeit führen.

In Visual C# 2005 erzeugt der Compiler nicht immer einen Fehler, wenn es für eine anonyme Methode keine eindeutige passende Überladung gibt. In Visual C# 2008 wird Compilerfehler CS0121 erzeugt. Dies ist im folgenden Beispiel dargestellt.

class Program
{
    static int ol_invoked = 0;
    delegate int D1(int x);
    delegate T D1<T>(T x);
    delegate T D1<T, U>(U u);
    static void F(D1 d1) { ol_invoked = 1; }
    static void F<T>(D1<T> d1t) { ol_invoked = 2; }
    static void F<T, U>(D1<T, U> d1t) { ol_invoked = 3; }
    static int Test001()
    {
// Produces CS0121:
        F(delegate(int x) { return 1; });         if (ol_invoked == 1)
            return 0;
        else
            return 1;
    }
    static int Main()
    {
        return Test001();
    }
}

29

Typfehler

Es wird nun ein Fehler erzeugt, wenn ein Array von Zeigern auf verwaltete Typen deklariert wird.

Unsichere Zeiger auf Referenztypen sind nicht zulässig und verursachen Compilerfehler. In Visual C# 2005 ist es möglich, ein Array von Zeigern auf verwaltete Typen zu deklarieren. In Visual C# 2008 wird Compilerfehler CS0208 erzeugt: "Es ist nicht möglich, einen Zeiger für den verwalteten Typ ('T') zu deklarieren oder dessen Adresse oder Größe abzurufen."

unsafe class TestClass<T>
{
// Produces CS0208:
    static T*[] x = { }; 
// Produces CS0208:
    static void Test(T*[] arr) 
    {
    }
// Produces CS0208:
    static T*[] TestB() 
    {
        return x;
    }

}

30

Überladungsauflösung

Es wird nun eine Warnung erzeugt, wenn sich Kandidaten für eine Überladungsauflösung nur durch ref oder out unterscheiden.

In Visual C# 2005 überprüft der C#-Compiler beim Auflösen von Überladungen für generische Typen nicht, ob die Typargumente dazu führen, dass die Kandidatenmethoden sich nur durch ref oder out unterscheiden. Dies hat zur Folge, dass die Methodenauswahl der Common Language Runtime (CLR) zur Laufzeit überlassen wird. Von dieser wird die erste Methode in der Liste ausgewählt. In Visual C# 2008 wird Compilerwarnung (Stufe 1) CS1956 erzeugt, wenn der Compiler feststellt, dass zwei Kandidatenmethoden für die Überladungsauflösung sich nur durch ref oder out unterscheiden. Diese Bedingung ist im folgenden Beispiel dargestellt.

using System;
class Base<T, S>
{
// Produces CS1956:
    public virtual void Test(out T x) 
    {
        Console.WriteLine("Test(out T x)");
        x = default(T);
    }
    public virtual void Test(ref S x)
    {
        Console.WriteLine("Test(ref T x)");
    }
}
interface IFace
{
    void Test(out int x);
}
class Derived : Base<int, int>, IFace
{
    static void Main()
    {
        IFace x = new Derived();
        int y;
        x.Test(out y);
    }

}

31

Typen, die NULL-Werte zulassen, und "??"-Ausdrücke

Ein Ausdruck, der den NULL-Sammeloperator mit NULL auf der linken Seite enthält, wird nicht mehr zu einer NULL-Konstanten ausgewertet.

In Visual C# 2005 wird ein Ausdruck, der den NULL-Sammeloperator mit NULL auf der linken Seite enthält, zu einer NULL-Konstanten ausgewertet. In Visual C# 2008 ist dies nicht mehr der Fall. In einigen Fällen führt das Visual C# 2005-Verhalten dazu, dass Variablen fälschlicherweise als definitiv zugewiesen behandelt werden. Der folgende Code kann in Visual C# 2005 ohne Fehler kompiliert und ausgeführt werden. In Visual C# 2008 wird jedoch Compilerfehler CS0165 erzeugt: "Verwendung der nicht zugewiesenen lokalen Variablen 'x'."

static void Main()
    {
        int x;
        if (null == (decimal?)(null ?? null)) x = 1;
        // Producers CS0165 in Visual C# 2008:
        System.Console.WriteLine(x);    
    }

Siehe auch

Weitere Ressourcen

Erste Schritte mit Visual C#

Änderungsprotokoll

Date

Versionsgeschichte

Grund

Juli 2008

Thema hinzugefügt.

SP1-Featureänderung.