다음을 통해 공유


Visual C# 2008의 주요 변경 사항

업데이트: 2008년 7월

Visual C# 2008 서비스 팩 1의 주요 변경 사항

다음 표에서는 Visual C# 2008의 최초 릴리스 버전 또는 Visual C# 2005에서 만든 응용 프로그램에 영향을 미칠 수 있는 Visual C# 2008 서비스 팩 1의 모든 주요 변경 사항을 보여 줍니다.

변경 번호

범주

문제

설명

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

Nullable 형식 및 ?? 식

컴파일러에서 이제 nullable 변수가 자신과 비교되는 식을 올바르게 계산합니다.

Visual C# 2008의 최초 릴리스 버전에서 다음 코드는 컴파일되고 런타임에 "false"가 출력됩니다. Visual C# 2008 서비스 팩 1에서는 컴파일러 경고(수준 3) CS1718이 생성되고 "true"가 출력됩니다.

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

4

반복기의 try-finally

break 문이 있는 반복기에서 중첩된 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

식 트리

식 트리에서 메서드 식을 잘못 boxing하는 일은 더 이상 발생하지 않습니다.

Visual C# 2008의 최초 릴리스 버전에서 다음 코드는 7, 0을 출력합니다. 즉 Console.WriteLine(e.Compile()(default(T))); 줄에서 S가 잘못 boxing되어 0이 출력됩니다. Visual C# 2008 SP1에서는 boxing이 일어나지 않으므로 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

개체 이니셜라이저

개체 이니셜라이저의 값 형식 초기화가 수정되었습니다.

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

식 트리

잘못된 식 트리에서 이제 올바른 예외를 throw합니다.

Visual C# 2008의 최초 릴리스 버전에서는 지정된 형식이 아닌 메서드에 대한 메서드 호출을 포함하는 식 트리에서 System.Security.VerificationException을 throw합니다. Visual C# 2008 SP1에서는 다음 코드에서 보듯이 System.ArgumentException이 throw됩니다.

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

특성

CharSet.Unicode는 이제 C#이 고정 배열 필드에 대해 생성하는 도우미 형식으로 전파됩니다.

C# 컴파일러는 고정 배열을 캡슐화하는 도우미 형식을 생성합니다. Visual C# 2008의 최초 릴리스 버전 및 이전 버전에서 배열의 레이아웃은 StructLayout 특성에 CharSet.Unicode가 지정된 경우에도 항상 ANSI입니다. C# 소스 코드에서 이를 바꿀 수 있는 방법은 없었습니다. Visual C# 2008 SP1에서는 다음 코드에서 보듯이 StructLayout 특성에 지정된 CharSet 값을 사용하여 도우미 클래스가 생성됩니다.

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 할당이 실패할 수 있습니다. 그 이유는 생성된 MSIL(Microsoft Intermediate Language)에서 배열의 길이를 각 요소의 크기로 곱할 때 확인되지 않는 mul 명령을 사용하기 때문입니다. Visual C# 2008 SP1에서는 mul 대신 mul.ovf 명령이 생성되므로 런타임에 할당을 시도할 때 오버플로가 발생하면 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# 캐스팅 의미 체계를 사용합니다.

System.Collections.ArrayList와 같은 제네릭이 아닌 컬렉션에 대한 LINQ 쿼리 식에서 컴파일러는 Cast<T> 연산자에 대한 호출을 포함하도록 쿼리의 from 절을 다시 씁니다. Cast<T>는 모든 요소 형식을 쿼리의 from 절에 지정된 형식으로 변환합니다. 또한 Visual C# 2008의 최초 릴리스 버전에서는 Cast<T> 연산자가 일부 값 형식 변환 및 사용자 정의 변환도 수행합니다. 하지만 이러한 변환은 표준 C# 의미 체계를 사용하는 대신 System.Convert 클래스를 사용하여 수행됩니다. 이러한 변환은 또한 특정 시나리오에서 심각한 성능 문제를 일으킬 수 있습니다. Visual C# 2008 SP1에서는 Cast<T> 연산자가 숫자 값 형식 및 사용자 정의 변환에 대해 InvalidCastException을 throw하도록 수정되었습니다. 이러한 변경을 통해 비표준 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# 2005에서 만든 응용 프로그램의 컴파일을 방해하거나 런타임 동작을 변경할 수 있는 Visual C# 2008 최초 릴리스 버전의 모든 주요 변경 사항을 보여 줍니다.

변경 번호

범주

문제

설명

12

형식 변환

이제 값이 0인 모든 상수 식을 열거형으로 변환할 수 있습니다.

리터럴 0은 암시적으로 모든 열거형 형식으로 변환될 수 있습니다. Visual C# 2005 및 이전 버전의 컴파일러에서도 0으로 계산되는 상수 식 중 암시적으로 열거형 형식으로 변환될 수 있는 식이 있지만 어떠한 식이 변환 가능한지 결정하는 규칙이 명확하지 않습니다. Visual C# 2008에서는 0과 일치하는 모든 상수 식은 암시적으로 열거형 형식으로 변환될 수 있습니다.

이로 인해 암시적 변환이 없다는 가정 아래 수행되는 메서드 오버로드 확인 등 기존 코드의 동작이 변경될 수도 있습니다. 다음 코드의 경우 Visual C# 2005 및 그 이전 컴파일러에서는 short 값에 대한 메서드 호출이 int 오버로드로만 확인되므로 문제 없이 컴파일됩니다. Visual C# 2008에서는 short 값이 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

형식 오류

구조체에서 참조 형식 멤버를 사용하는 것에 대한 새로운 경고가 추가되었습니다.

구조체에 대한 확정 할당 규칙에 따르면 구조체는 해당 형식의 기존 인스턴스로 설정되거나 참조되기 전에 모든 멤버가 할당되어야 합니다. 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

고정 크기 버퍼

버퍼에 값을 할당하기 전에 안전하지 않은 구조체의 고정 크기 버퍼에 액세스하면 이제 컴파일러 오류가 발생합니다.

안전하지 않은 포인터에 대한 확정 할당 규칙에 따르면 포인터의 참조를 해제하기 전에 먼저 포인터를 설정해야 합니다. Visual C# 2005에서는 안전하지 않은 구조체에 배열에 대한 포인터가 포함되어 있는 경우 이 포인터에 값을 할당하기 전에 액세스해도 컴파일러 오류가 발생하지 않았습니다. 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

이제 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

반복기의 try-finally

이제 try 블록의 반복기가 continue 또는 goto로 이스케이프될 때 finally 블록이 실행됩니다.

Visual C# 2005에서는 try-finally 구문의 try 블록에서 goto 또는 continue 문을 통해 제어가 반복기 블록을 빠져 나오는 경우 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(European Computer Manufacturers Association) 사양에 맞지 않습니다. 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에서는 "사용되지 않는 'A.Filename' 멤버가 사용되는 'Error.Filename' 멤버를 재정의합니다."라는 컴파일러 경고 컴파일러 경고(수준 1) CS0809가 생성됩니다. 다음 예제에서는 이 경고가 발생합니다.

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

빌드 오류

/debug를 사용하지 않고 /pdb 컴파일러 옵션을 사용하면 이제 오류가 발생합니다.

Visual C# 2005에서는 /pdb 옵션을 지정하고 /debug 옵션을 지정하지 않아도 경고나 오류가 표시되지 않습니다. Visual C#에서는 .pdb 파일을 생성하지 않고 릴리스 빌드를 만듭니다. Visual C# 2008의 최초 릴리스 버전에서는 /debug를 지정하지 않고 /pdb를 지정하면 컴파일러에서 컴파일러 오류 CS2036을 표시합니다.

23

형식 오류

switch 조건이 void이면 이제 오류가 생성됩니다.

Visual C# 2005에서는 switch 문에서 void 메서드 호출이 사용될 때 오류가 생성되지 않습니다. Visual C# 2008에서는 컴파일러 오류 CS0151이 생성됩니다.

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

}

24

오버플로 검사

상수 decimal을 정수로 변환하면 이제 다른 컴파일러 오류가 발생합니다.

Visual C# 2005에서 다음 코드는 "'b'에 할당할 식은 상수여야 합니다."라는 컴파일러 오류 CS0133을 생성합니다.

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

Visual C# 2008에서는 "'256M' 상수 값을 'byte'(으)로 변환할 수 없습니다."라는 컴파일러 오류 CS0031이 생성됩니다. 이 오류는 unchecked 한정자가 적용되어 있는 경우에도 생성됩니다.

25

상수 식

상수 식과 관련된 사양이 더 엄격하게 준수됩니다.

Visual C# 2008에서는 Visual C# 2005에서 상수 식에 잘못된 연산자 및 변수를 허용하던 몇 가지 문제가 수정되었습니다. 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

형식 오류

이제 정적 형식을 대리자 또는 람다 식에 매개 변수로 사용하면 오류가 발생합니다.

Visual C# 2005에서는 정적 형식을 대리자 또는 무명 메서드에 매개 변수로 사용해도 오류가 발생하지 않습니다. 정적 형식은 인스턴스화할 수 없으므로 메서드 매개 변수의 형식으로 사용할 수 없습니다. 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

Nullable 형식 및 ?? 식

상수를 더 넓은 형식의 nullable에 할당하기 전에 nullable 형식으로 캐스팅해도 경고가 발생하지 않습니다.

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

형식 오류

이제 관리되는 형식에 대한 포인터 배열을 선언하면 오류가 발생합니다.

참조 형식에 대한 안전하지 않은 포인터는 허용되지 않으며 컴파일러 오류가 발생합니다. Visual C# 2005에서는 관리되는 형식에 대한 포인터 배열을 선언할 수 있습니다. Visual C# 2008에서는 "관리되는 형식('T')의 주소 또는 크기를 가져오거나 해당 형식에 대한 포인터를 선언할 수 없습니다."라는 컴파일러 오류 CS0208이 발생합니다.

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만 다르게 되는지 확인하지 않습니다. 따라서 메서드 선택은 런타임에 CLR(공용 언어 런타임)에 맡겨지며 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

Nullable 형식 및 ?? 식

왼쪽이 null인 null 병합 식은 더 이상 null 상수로 계산되지 않습니다.

Visual C# 2005에서 왼쪽이 null인 null 병합 식은 null 상수로 계산됩니다. Visual C# 2008에서는 그렇지 않습니다. 이러한 Visual C# 2005의 동작으로 인해 변수가 확정 할당 변수로 잘못 처리되는 경우가 발생할 수 있습니다. 다음 코드는 Visual C# 2005에서 오류 없이 컴파일되고 실행되지만 Visual C# 2008에서는 "할당되지 않은 'x' 지역 변수를 사용했습니다."라는 컴파일러 오류 CS0165가 발생합니다.

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 기능 변경