共用方式為


Visual C# 2010 的重大變更

下表列出 Visual C# 2010 的重大變更,這些變更可能造成以 Visual C# 2008 建立的應用程式無法編譯,或導致執行階段行為改變。

分類

問題

描述

組件繫結

組件繫結會將兩個組件視為相等。

參考特定參考組件之 .NET Framework 版本和 .NET Framework for Silverlight 版本並同時使用 extern alias 的 C# 2010 應用程式會造成編譯器錯誤。 根據預設,組件繫結會將兩個組件視為相等。

若要解決錯誤,請使用 /appconfig 編譯器選項指定 app.config 檔的位置,這個檔案會停用使用 <supportPortability> 標記的預設行為。 如需詳細資訊,請參閱 /appconfig (C# 編譯器選項)

如果您使用 Microsoft Build Engine (MSBuild) 繫結應用程式,請將適當的標記加入至 .csproj 檔。

共變數和反變數

泛型介面及委派 (例如 IEnumerable<T>Func<TResult>) 中加入了新的隱含轉換。

泛型介面及委派 (例如 IEnumerable<T>Func<TResult>) 現在已有泛型型別引數的隱含轉換。 例如,在 C# 2010 中,IEnumerable<string> 可以隱含轉換為 IEnumerable<object>,這樣可能在下列情節中造成不同的行為。

如需詳細資訊,請參閱共變數和反變數 (C# 和 Visual Basic)

Null 聯合運算子

Null 聯合運算子 (??) 不允許未指派的區域變數。

在 C# 2010 中,即使左邊的運算元保證不會是 null,您仍無法使用未指派的區域變數做為 null 聯合運算子右邊的運算元。

例如,下列程式碼在 C# 2008 中會進行編譯,但是在 C# 2010 中會產生編譯器錯誤 CS0165

int? i;
int? j;
int? x = (i = 2) ?? j;

方法群組型別推斷

編譯器可以推斷方法群組的泛型和非泛型委派,但可能造成模稜兩可的情況。

在 C# 2008 中,編譯器無法推斷方法群組的泛型委派。 因此,它會固定使用非泛型委派 (如果有的話)。

在 C# 2010 中會推斷方法群組的泛型和非泛型委派,而編譯器同樣可能會推斷任一種。 如果您同時有泛型和非泛型版本的委派,而且兩者都符合需求,則可能造成模稜兩可的情況。 例如,下列程式碼在 C# 2008 中會進行編譯,並且呼叫使用非泛型委派的方法。 在 C# 2010 中,此程式碼會產生編譯器錯誤,回報模稜兩可的呼叫。

public class Sample
{
    delegate string NonGenericDelegate();
    delegate T GenericDelegate<T>();
    string UseDelegate(NonGenericDelegate del)
    {
        return null;
    }

    T UseDelegate<T>(GenericDelegate<T> del)
    {
       return default(T);
    }

    public string Test()
    {
       // This line produces 
       // a compiler error in C# 2010.
       return UseDelegate(Test);
    }
}

選擇性參數

現在 C# 會辨識 OptionalAttribute,其可能造成方法多載解析發生變更。

在 C# 2008 中,編譯器會忽略 OptionalAttribute,因為 C# 不支援選擇性參數。

C# 2010 則採用選擇性參數。 您可以使用新的語言語法或使用 OptionalAttribute 來宣告選擇性參數。 如果您在 C# 2008 中使用 OptionalAttribute 與其他支援選擇性參數的語言互通 (例如 Visual Basic),則 C# 2008 固定只會選擇在方法呼叫中列出所有參數的方法。 C# 2010 可能會挑選擁有選擇性參數的方法,即使這些參數並未在方法呼叫中指定。

下列程式碼在 C# 2008 中會從基底類別呼叫方法,因為選擇性屬性會加以忽略,而且編譯器會當做衍生類別中的方法永遠需要字串參數。 在 C# 2010 中,程式碼會從衍生類別呼叫方法,因為現在這個方法簽章符合方法呼叫。

class Program
{
    public static void Main(string[] args)
    {
        var obj = new Derived();
        obj.Method();
    }
}

class Base
{
    public void Method() 
    { 
        Console.WriteLine(
            "Base class + no optional parameters"); 
    }
}

class Derived : Base
{
    public void Method(
        [Optional][DefaultParameterValue("Hello")] 
        string s) 
    { 
        Console.WriteLine(
            "Derived class + an optional parameter");
    }
}
// Prints different results.
// C# 2008: Base class + no optional parameters
// C# 2010: Derived class + an optional parameter

如需詳細資訊,請參閱具名和選擇性引數 (C# 程式設計手冊)

內嵌的 Interop 型別

嘗試使用 CoClass 建立內嵌 COM 型別的執行個體將會導致編譯器錯誤。

在 C# 2010 中,當您加入 Interop 組件的參考 (例如 Microsoft.Office.Interop.WordMicrosoft.Office.Interop.Excel) 時,就會內嵌此組件的型別。 如需詳細資訊,請參閱逐步解說:從 Managed 組件內嵌型別 (C# 和 Visual Basic)/link (C# 編譯器選項)

當您在程式碼中建立內嵌 COM 型別的執行個體時,必須使用適當的介面來建立執行個體。 如果您嘗試使用 CoClass 建立內嵌 COM 型別的執行個體,則編譯器會回報錯誤。

// Add the following statement
// at the beginning of the file:
// using Word = Microsoft.Office.Interop.Word;
// This statement does not compile in C# 2010.
Word.Application wordClass = 
    new Word.ApplicationClass();
// Use the following code instead.
Word.Application wordInterface = 
    new Word.Application();

內嵌的 Interop 型別

get_ 和 set_ 方法無法存取索引的屬性。

當您內嵌 COM 型別時,所有 COM 物件的呼叫都會動態分派。 如下列程式碼範例所示,如果您嘗試使用 get_Range 方法存取索引的屬性 Range,C# 執行階段繫結器會在類別中尋找使用者定義的 get_Range 方法,而這個方法並不存在。 為避免發生這個問題,請針對索引的屬性使用 C# 2010 語法。 如需詳細資訊,請參閱 HOW TO:使用 Visual C# 2010 功能存取 Office Interop 物件 (C# 程式設計手冊)

// Add the following statement
// at the beginning of the file:
// using Excel = Microsoft.Office.Interop.Excel;
Excel.Application excelApp = new Excel.Application();
excelApp.Visible = true;
excelApp.Workbooks.Add(
    Excel.XlWBATemplate.xlWBATWorksheet);
Excel.Worksheet sheet = 
    excelApp.ActiveSheet as Excel.Worksheet;
// The following statement throws 
// a run-time excpetion in C# 2010.
Excel.Range rangeOld = 
    sheet.get_Range(
        sheet.Cells[1, 1], sheet.Cells[2, 2]);
// Use the following syntax instead.
Excel.Range rangeNew = 
    sheet.Range[sheet.Cells[1, 1], 
                sheet.Cells[2, 2]];

事件同步

現在可以透過使用 CompareExchange 方法,在編譯器產生的加入和移除方法中同步寫入事件的支援欄位。 這樣可能導致競爭情況。

在 C# 2010 中,現在可以透過使用 CompareExchange 方法而非 MethodImplAttribute,同步變更編譯器產生之加入和移除方法的支援欄位。

這樣可能造成 C# 2008 中所沒有的競爭情況,如下列程式碼範例所示。

using System;
using System.Threading;

class Sample
{
    public event Action sampleEvent;

    static void Main()
    {
        new Sample().Loop();
    }

    void Loop()
    {
        new Thread(() => Test.Method(this)).Start();
        while (true)
        {
            lock (this)
            {
                if (sampleEvent != null)
                {
                    // In C# 2010, sampleEvent 
                    // can be null here,
                    // which causes 
                    // a run-time exception.
                    sampleEvent();
                }
            }
        }
    }
}

class Test
{
    public static void Method(Sample arg)
    {
        while (true)
        {
            arg.sampleEvent += Method;
            arg.sampleEvent -= Method;
        }
    }
    static void Method() { }
}

為避免發生競爭情況,請依照下列程式碼範例所示修改 Loop 方法。

void Loop()
{
   new Thread(() => Test.Method(this)).Start();
   while (true)
   {
       lock (this)
       {
           // Create a local copy of the delegate.
           Action local = sampleEvent;
           if (local != null)
           {
               local();
           }
        }
    }
}

請參閱

其他資源

Visual C# 使用者入門

MSBuild