共用方式為


Visual C# 2008 的重大變更

更新: 2008 年 7 月

Visual C# 2008 Service Pack 1 的重大變更

下列表格列出在 Visual C# 2008 Service Pack 1 中,可能對使用 Visual C# 2008 原始版本或 Visual C# 2005 建立的應用程式造成影響的所有重大變更。

變更編號

類別

變更

說明

1

多載解析

型別推斷現在包含在方法多載解析中的指標型別陣列上。

在 Visual C# 2008 及更早的版本中,型別推斷會造成方法多載解析程序將指標型別陣列排除在外。在下列程式碼中,Visual C# 2005 編譯器選取了 Test 的非泛型版本,因為 Test 由於本身的 int*[] 型別參數考量而被排除在外。在 Visual C# 2008 中,則會選取 Test 的泛型版本。

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

索引子

現在除了方法,編譯器還會對索引子和屬性產生錯誤 CS0466。

在 Visual C# 2008 的原始版本和較早版本中,可以定義索引子的明確實作,其中實作會有 params 參數,但介面定義則沒有。這個語法結構與規格背道而馳。在 Visual C# 2008 SP1 中,這樣的語法結構則會產生編譯器錯誤 CS0466,如下列程式碼所示。

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

}

3

可為 Null 的型別 (Nullable Type) 和 ?? 運算式

編譯器現在會正確評估可為 Null 的變數與本身比較的運算式。

在 Visual C# 2008 的原始版本中,下列程式碼會編譯並在執行階段輸出 "false";在 Visual C# 2008 Service Pack 1 中,這些程式碼則會產生編譯器警告 (層級 3) CS1718,並輸出 "true"。

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

4

Iterator 中的 try-finally

由具有 break 陳述式的 Iterator 執行巢狀 finally 區塊的程序已改變。

在 Visual C# 2008 的原始版本中,下列程式碼會執行外部 finally 兩次;在 Visual C# 2008 SP1 中,外部 finally 只會執行一次。

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

運算式樹狀架構

運算式樹狀架構中將不會再發生不正確的方法運算式 Box 作業。

在 Visual C# 2008 的原始版本中,下列程式碼會輸出 7, 0。Console.WriteLine(e.Compile()(default(T))); 這一行會輸出 0,因為 S 會執行錯誤的 Box 作業;在 Visual C# 2008 SP1 中則不會發生 Box 的作業,且程式會輸出 7, 7。

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

物件初始設定式

物件初始設定式中的實值型別 (Value Type) 初始化已修正。

在 Visual C# 2008 的原始版本中,下列範例中的區域變數 b 不會正確初始化,且其成員 X 值為 0;在 Visual C# 2008 SP1 中,S.X 在兩個 new 運算式中都會正確初始化為 1。

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

型別轉換

Null 常值不再能轉換為列舉值。

在 Visual C# 2008 的原始版本中,null 常值在某些情況中允許轉換為列舉值。在 Visual C# 2008 SP1 中如果嘗試這樣做,會產生編譯器錯誤 CS1502編譯器錯誤 CS1503,如下列範例所示。

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

運算式樹狀架構

無效的運算式樹狀架構現在會擲回正確的例外狀況。

在 Visual C# 2008 的原始版本中,當運算式樹狀架構包含的方法呼叫,是針對不在指定型別上的方法,會擲回 System.Security.VerificationException;在 Visual C# 2008 SP1 中,則會擲回 System.ArgumentException,如下列程式碼所示。

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 現在會散佈至 C# 為固定陣列欄位產生的 Helper 型別。

C# 編譯器會產生 Helper 型別以封裝固定陣列。在 Visual C# 2008 的原始版本及較早版本中,就算 StructLayout 屬性指定 CharSet.Unicode,陣列的配置仍永遠是 ANSI。沒有任何辦法能在 C# 原始程式碼中對此進行變更。在 Visual C# 2008 SP1 中,會使用在 StructLayout 屬性中指定的任何 CharSet 值建構 Helper 類別,如下列程式碼所示。

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

溢位檢查

stackalloc 現在會執行溢位檢查。

在 Visual C# 2008 的原始版本中,stackalloc 配置可能在沒有造成任何例外狀況的情況下發生失敗。這是當陣列的長度乘以每個元素的大小時,在產生的 Microsoft Intermediate Language (MSIL) 中之不檢查的 mul 指令所造成。在 Visual C# 2008 SP1 中,會產生 mul.ovf 指令而非 mul,所以在執行階段中嘗試配置時,溢位會產生 System.OverflowEx ception。

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

標準查詢運算子

對非泛型集合的查詢現在會使用標準的 C# 轉型語意。

在對非泛型集合的 LINQ 查詢運算式中 (例如 System.Collections.ArrayList),編譯器會重寫查詢的 from 子句,以包含對 Cast<T> 運算子的呼叫。Cast<T> 會將所有項目型別轉換為查詢中 from 子句內所指定的型別。此外,在 Visual C# 2008 的原始版本中,Cast<T> 運算子也會執行一些實值型別轉換與使用者定義的轉換。然而,這些轉換是使用 System.Convert 類別執行,而非使用標準 C# 語意。在特定情況中,這些轉換也會造成嚴重的效能問題。在 Visual C# 2008 SP1 中,Cast<T> 運算子已經過修改,會對數值型別與使用者定義的轉換擲回 InvalidCastException。這項變更同時排除了非標準 C# 轉型語意和效能的問題。下列範例說明了這項變更。

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

Visual C# 2008 原始版本的重大變更

下列表格列出在 Visual C# 2008 原始版本中,可能造成使用 Visual C# 2005 建立的應用程式無法進行編譯,或導致執行階段行為改變的所有重大變更。

變更編號

類別

變更

說明

12

型別轉換

現在允許將任何值為 0 的常數運算式轉換為列舉。

常值 0 可以隱含轉換為任何列舉型別。在 Visual C# 2005 和較早版本的編譯器中,也有一些評估為 0 的常數運算式可以隱含轉換為任何列舉型別,但決定哪些運算式可以轉換的規則並不清楚。在 Visual C# 2008 中,所有等於 0 的常數運算式都能隱含轉換為任何列舉型別。

這可能造成現有程式碼的一些行為改變,例如仰賴沒有隱含轉換的情況的方法多載解析。下列程式碼會在 Visual C# 2005 和較早版本的編譯器上成功編譯,將短值上的方法引動只解析為 int 多載。在 Visual C# 2008 中,由於短值也能隱含轉換為 E,因此這項引動是模稜兩可的。在 Visual C# 2008 中,這項行為已經改變為允許任何評估為 0 的常數運算式進行轉換。

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

屬性

現在當同樣的 TypeForwardedTo 屬性在一個組件中出現兩次時會發生錯誤。

在 Visual C# 2005 中,當組件中包含兩個以同樣型別為目標的 System.Runtime.CompilerServices.TypeForwardedTo 屬性時,並不會產生錯誤。在 Visual C# 2008 中,則會產生編譯器錯誤 CS0739,如下列範例所示。

// 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

型別錯誤

加入了關於在結構 (Struct) 中使用參考型別成員的新警告。

結構的明確指派規則,需要將結構設定至其型別的現有執行個體,或者在結構的每個成員受到參考前都先進行指派。在 Visual C# 2005 中使用結構未指派的參考型別成員時,不會產生警告或錯誤;在 Visual C# 2008 中,則會產生編譯器警告 (層級 1) CS1060,如下列範例所示。

    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

溢位檢查

const decimal 型別的範圍檢查已更正。

在 Visual C# 2005 中,當您對 const decimal 型別進行轉型,範圍檢查不一定總是正確,而且可能造成不正確的編譯器錯誤。在 Visual C# 2008 中,下列程式碼會產生正確的錯誤:編譯器錯誤 CS0031

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

16

溢位檢查

轉換為 long 時超出範圍的情況,現在會產生正確的編譯器錯誤。

在 Visual C# 2005 中,下列程式碼不會產生編譯器錯誤。在 Visual C# 2008 中,則會產生編譯器錯誤 CS0031

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

17

固定大小緩衝區

在對緩衝區指派值前存取 unsafe 結構中的固定大小緩衝區,現在會產生編譯器錯誤。

unsafe 指標的明確指派規則,要求在對指標進行取值前先設定指標。在 Visual C# 2005 中,當 unsafe 結構包含陣列的指標,在對指標指派值前先加以存取並不會產生編譯器錯誤。在 Visual C# 2008 中,這麼做則會產生編譯器錯誤 CS0165,如下列程式碼所示。

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

18

現在副作用 (Side Effect) 會保留在 null 聯合運算式中。

明確指派與 ?? 運算子。

在 Visual C# 2005 中,在某些情況下,null 聯合運算式左邊的副作用不會保留。在這些情況中,下列範例中的第二個 Console.WriteLine 陳述式會產生一個不正確的編譯器錯誤,指出 b 未指派。在 Visual C# 2008 中,同樣的程式碼會正確編譯,不會產生錯誤。

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

}

19

Iterator 中的 try-finally

現在當 try 區塊中的 Iterator 以 continue 或 goto 逸出時,會執行 finally 區塊。

在 Visual C# 2005 中,在 try-finally 建構中,當控制項使用 goto 或 continue 陳述式傳出 try 區塊中的 Iterator 區塊,finally 區塊不會執行。在 Visual C# 2008 中,當發生這些情況時 finally 區塊會執行。

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

基底類別和介面

類別建構現在會忽略基底類別中相同介面成員的明確實作。

在 Visual C# 2005 中,當類別沒有提供介面成員的實作時,編譯器會取代基底類別實作,就算基底類別實作是宣告為明確介面實作也一樣。這項行為不符合歐洲電腦製造商協會 (ECMA) 規格的要求。Visual C# 2008 則會正確實作這項規格。下列範例中,Visual C# 2005 會列印 "B.Test";Visual C# 2008 會正確列印 "A.Test",並忽略類別 B 中的 Test 方法,因為此方法為明確的介面實作。

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

屬性

使用過時的成員現在會產生編譯器警告。

您可以使用 Obsolete 屬性標記方法,則當這些方法在編譯時期受到叫用時,就會產生錯誤或警告。當您將此屬性放在虛擬方法上時,屬性必須放置在基底方法上。如果 Obsolete 屬性放置在覆寫方法上,在引動時就不會造成編譯器錯誤或警告。在 Visual C# 2005 中,編譯器允許您將 Obsolete 屬性放置在覆寫方法上,雖然當此屬性放在這個位置時沒有任何作用。在 Visual C# 2008 中,這樣做會產生編譯器警告編譯器警告 (層級 1) CS0809:「過時的成員 'A.Filename' 會覆寫非過時的成員 'Error.Filename'」。下列範例會造成此警告。

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

建置錯誤

現在使用 /pdb 編譯器選項時,如果沒有 /debug,將會產生錯誤。

在 Visual C# 2005 中,當您指定 /pdb 選項而未指定 /debug 選項時,不會顯示任何警告或錯誤。Visual C# 只會建立發行的組建 (Release Build),不會產生 .pdb 檔案。在 Visual C# 2008 的原始版本中,如果您指定 /pdb 而沒有指定 /debug,編譯器會顯示編譯器錯誤 CS2036

23

型別錯誤

現在當 switch 條件為虛值 (Void) 時,會產生錯誤。

在 Visual C# 2005 中,當 void 方法引動用於 switch 陳述式中時,並不會產生錯誤;在 Visual C# 2008 中,會產生編譯器錯誤 CS0151

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

}

24

溢位檢查

現在將十進位常數轉換為整數時,會產生不同的編譯器錯誤。

在 Visual C# 2005 中,下列程式碼會產生編譯器錯誤 CS0133:「指派給 'b' 的運算式必須為常數」。

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

在 Visual C# 2008 中,會產生編譯器錯誤 CS0031:「無法將常數值 '256M' 轉換為 'byte'」。請注意就算套用 unchecked 修飾詞也會產生此錯誤。

25

常數運算式

更準確符合常數運算式的相關規格。

在 Visual C# 2005 中,會造成允許運算子和變數存在於常數運算式中之錯誤行為的幾項問題,在 Visual C# 2008 中都已經更正。在 Visual C# 2005 中,下列程式碼會正常編譯,不會造成錯誤。在 Visual C# 2008 中,則會產生編譯器錯誤 CS0165編譯器警告 (層級 1) CS0184編譯器警告 (層級 3) CS1718

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

型別錯誤

現在當靜態型別在委派或 Lambda 運算式中做為參數時,會產生錯誤。

在 Visual C# 2005 中,如果使用靜態型別做為委派或匿名方法 (Anonymous Method) 的參數,並不會產生錯誤。靜態型別由於無法具現化,因此不能做為方法參數的型別。Visual C# 2005 版本的編譯器,允許將靜態型別做為委派和匿名方法宣告中的參數型別。如果您傳遞 null 做為參數,這樣的委派可以受到叫用。在 Visual C# 2008 中,如果使用靜態型別做為委派或匿名方法的參數,會產生錯誤編譯器錯誤 CS0721,如下列範例所示。

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

可為 Null 的型別和 ?? 運算式

在您指派常數為可為 Null 前 (屬於範圍更廣的型別),將此常數轉型為可為 Null 的型別並不會產生警告。

在 Visual C# 2005 中,下列程式碼會產生編譯器警告 (層級 3) CS0219。在 Visual C# 2008 中,則不會產生警告。

ushort? usq2 = (byte?)0;

28

多載解析

現在當匿名方法上發生模稜兩可的多載解析時會產生錯誤。

多載方法上的方法引動必須由編譯器解析,以判斷要叫用的特定多載。當引動的參數型別受到部分推斷時,要叫用的特定多載可能變得模稜兩可。這樣會造成編譯器錯誤。

在傳遞匿名方法做為委派參數的情況中,匿名方法的委派型別會受到部分推斷。當編譯器要選取正確的多載時,這可能會造成模稜兩可。

在 Visual C# 2005 中,當無法找出匿名方法的單一最佳多載時,編譯器不一定會產生錯誤。在 Visual C# 2008 中,則會產生編譯器錯誤 CS0121,如下列範例所示。

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

型別錯誤

現在如果您對 Managed 型別宣告指標陣列,將會產生錯誤。

不能有對參考型別的 Unsafe 指標,且這些指標會造成編譯器錯誤。在 Visual C# 2005 中,可以對 Managed 型別宣告指標陣列。在 Visual C# 2008 中,則會產生編譯器錯誤 CS0208:「不能取得 Managed 型別 ('T') 的位址、大小,也不能宣告指向它的指標」。

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

}

30

多載解析

現在當多載解析候選方法只有 ref 或 out 相異時,會產生警告。

在 Visual C# 2005 中,當 C# 編譯器在泛型型別上執行多載解析時,並不會驗證型別引數是否會造成候選方法只有 ref 或 out 相異。因此,對方法的選擇會留到執行階段,交由 Common Language Runtime (CLR) 執行,且會直接選取清單中的第一個方法。在 Visual C# 2008 中,當編譯器偵測到多載解析的兩個候選方法只有 ref 或 out 相異時,會產生編譯器警告 (層級 1) CS1956。下列範例說明了這個條件。

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

可為 Null 的型別與 ?? 運算式

null 在左側的 null 聯合運算式不再評估為 null 常數。

在 Visual C# 2005 中,null 在左側的 null 聯合運算式會評估為 null 常數。在 Visual C# 2008 中將不會再有這種情況。在某些情況中,Visual C# 2005 的行為會讓變數錯以明確指派的情況處理。下列程式碼在 Visual C# 2005 中會正常編譯與執行,不會產生錯誤,但在 Visual C# 2008 中則會產生編譯器錯誤 CS0165:「使用未指定的區域變數 'x'」。

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

請參閱

其他資源

Visual C# 使用者入門

變更記錄

日期

記錄

原因

2008 年 7 月

加入主題。

SP1 功能變更。