Visual C# 2010의 주요 변경 사항

다음 표에서는 Visual C# 2008에서 만든 응용 프로그램이 컴파일되지 않거나 런타임 동작이 달라질 수 있는 Visual C# 2010의 주요 변경 사항을 보여 줍니다.

범주

문제

설명

어셈블리 바인딩

어셈블리 바인딩이 두 개의 어셈블리를 동일한 것으로 처리합니다.

특정 참조 어셈블리의 .NET Framework 버전과 .NET Framework for Silverlight 버전을 동시에 참조하고 extern alias도 사용하는 C# 2010 응용 프로그램에서는 컴파일러 오류가 발생합니다. 기본적으로 어셈블리 바인딩은 두 개의 어셈블리를 동일한 것으로 처리합니다.

이 문제를 해결하려면 /appconfig 컴파일러 옵션을 사용하여 app.config 파일의 위치를 지정합니다. 이 파일은 <supportPortability> 태그를 사용하여 기본 동작을 사용하지 않도록 설정합니다. 자세한 내용은 /appconfig (C# 컴파일러 옵션)를 참조하십시오.

MSBuild(Microsoft Build Engine)를 사용하여 응용 프로그램을 빌드하는 경우에는 .csproj 파일에 적절한 태그를 추가합니다.

공 분산 및 반공변성(Contravariance)

IEnumerable<T>Func<TResult>와 같은 제네릭 인터페이스와 대리자에 새로운 암시적 변환이 추가되었습니다.

IEnumerable<T>Func<TResult>와 같은 제네릭 인터페이스와 대리자에는 이제 제네릭 형식의 인수에 대한 암시적 변환이 포함됩니다. 예를 들어 C# 2010의 경우 IEnumerable<string>을 IEnumerable<object>로 암시적으로 변환할 수 있으므로 다음 시나리오에서 다른 동작이 발생할 수 있습니다.

자세한 내용은 공변성(Covariance) 및 반공변성(Contravariance)(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에서는 C#에서 선택적인 매개 변수가 지원되지 않기 때문에 컴파일러가 OptionalAttribute를 무시합니다.

C# 2010에는 선택적인 매개 변수가 도입되었습니다. 새 언어 구문을 사용하거나 OptionalAttribute를 사용하여 선택적인 매개 변수를 선언할 수 있습니다. 선택적인 매개 변수를 지원하는 다른 언어(예: Visual Basic)와의 호환성을 위해 C# 2008에서 OptionalAttribute를 사용할 경우 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의 경우 Microsoft.Office.Interop.Word 또는 Microsoft.Office.Interop.Excel과 같은 interop 어셈블리에 참조를 추가하면 이 어셈블리의 형식이 포함됩니다. 자세한 내용은 연습: 관리되는 어셈블리의 형식 포함(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 구문을 사용합니다. 자세한 내용은 방법: 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의 경우 컴파일러에서 생성된 추가 및 제거 메서드에 대해 지원 필드를 변경하기 위한 동기화 작업은 MethodImplAttribute 대신 CompareExchange 메서드를 사용하여 수행됩니다.

이 경우 다음 코드 예제에서와 같이 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