C# 6의 새로운 기능What's New in C# 6

C#의 6.0 릴리스에는 개발자의 생산성을 개선하는 많은 기능이 추가되었습니다.The 6.0 release of C# contained many features that improve productivity for developers. 이러한 기능의 전반적인 영향은 더 읽기 쉬운 더 간결한 코드를 작성한다는 것입니다.The overall effect of these features is that you write more concise code that is also more readable. 많은 일반적인 사례에 대한 더 적은 의례가 구문에 포함됩니다.The syntax contains less ceremony for many common practices. 더 적은 의례로 디자인 의도를 더 쉽게 파악할 수 있습니다.It's easier to see the design intent with less ceremony. 이러한 기능을 자세히 알아보세요. 생산성을 높이고 더 가독성이 높은 코드를 작성할 수 있을 것입니다.Learn these features well, and you'll be more productive and write more readable code. 언어의 구문보다 기능에 더 집중할 수 있습니다.You can concentrate more on your features than on the constructs of the language.

이 문서의 나머지 부분에서는 이러한 각 기능을 간략히 설명하고 이러한 기능을 살펴볼 수 있는 링크를 제공합니다.The rest of this article provides an overview of each of these features, with a link to explore each feature. 또한 자습서 섹션의 C# 6에 대한 대화형 살펴보기에서 기능을 살펴볼 수도 있습니다.You can also explore the features in an interactive exploration on C# 6 in the tutorials section.

읽기 전용 auto 속성Read-only auto-properties

읽기 전용 auto 속성은 변경할 수 없는 형식을 만드는 더 간결한 구문을 제공합니다.Read-only auto-properties provide a more concise syntax to create immutable types. get 접근자만 사용하여 auto 속성을 선언합니다.You declare the auto-property with only a get accessor:

public string FirstName { get; }
public string LastName { get;  }

FirstNameLastName 속성은 동일한 클래스의 생성자 본문에서만 설정할 수 있습니다.The FirstName and LastName properties can be set only in the body of the constructor of the same class:

public Student(string firstName, string lastName)
{
    if (IsNullOrWhiteSpace(lastName))
        throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));
    FirstName = firstName;
    LastName = lastName;
}

다른 메서드에서 LastName을 설정하려고 하면 CS0200 컴파일 오류가 생성됩니다.Trying to set LastName in another method generates a CS0200 compilation error:

public class Student
{
    public string LastName { get;  }

    public void ChangeName(string newLastName)
    {
        // Generates CS0200: Property or indexer cannot be assigned to -- it is read only
        LastName = newLastName;
    }
}

이 기능은 변경할 수 없는 형식을 만들기 위한 실제 언어 지원을 구현하고, 더 간결하고 편리한 auto 속성 구문을 사용합니다.This feature enables true language support for creating immutable types and uses the more concise and convenient auto-property syntax.

이 구문을 추가해도 액세스 가능 메서드가 제거되지 않으면 이진 호환 가능 변경입니다.If adding this syntax doesn't remove an accessible method, it's a binary compatible change.

Auto 속성 이니셜라이저Auto-property initializers

‘Auto 속성 이니셜라이저’를 통해 속성 선언의 일부로 auto 속성의 초기 값을 선언할 수 있습니다. Auto-property initializers let you declare the initial value for an auto-property as part of the property declaration.

public ICollection<double> Grades { get; } = new List<double>();

Grades 멤버는 선언된 위치에서 초기화됩니다.The Grades member is initialized where it's declared. 따라서 더 쉽게 정확히 한 번 초기화를 수행할 수 있습니다.That makes it easier to perform the initialization exactly once. 초기화가 속성 선언의 일부이므로 Student 개체에 대한 public 인터페이스를 통해 스토리지를 균등하게 할당하기 쉽습니다.The initialization is part of the property declaration, making it easier to equate the storage allocation with the public interface for Student objects.

식 본문 함수 멤버Expression-bodied function members

개발자가 작성하는 많은 멤버는 단일 식이 될 수 있는 단일 명령문입니다.Many members that you write are single statements that could be single expressions. 대신 식 본문 멤버를 작성하세요.Write an expression-bodied member instead. 이 내용은 메서드 및 읽기 전용 속성에 적용됩니다.It works for methods and read-only properties. 예를 들어 ToString()의 재정의가 좋은 예입니다.For example, an override of ToString() is often a great candidate:

public override string ToString() => $"{LastName}, {FirstName}";

또한 읽기 전용 속성에 이 구문을 사용할 수 있습니다.You can also use this syntax for read-only properties:

public string FullName => $"{FirstName} {LastName}";

기존 멤버를 식 본문 멤버로 변경하는 것은 이진 호환 가능 변경입니다.Changing an existing member to an expression bodied member is a binary compatible change.

using staticusing static

using static 향상된 기능을 사용하여 단일 클래스의 정적 메서드를 가져올 수 있습니다.The using static enhancement enables you to import the static methods of a single class. 사용 중인 클래스를 지정합니다.You specify the class you're using:

using static System.Math;

Math에는 인스턴스 메서드가 포함되어 있지 않습니다.The Math does not contain any instance methods. using static을 사용하여 정적 및 인스턴스 메서드가 둘 다 포함된 클래스에 대한 클래스의 정적 메서드를 가져올 수도 있습니다.You can also use using static to import a class' static methods for a class that has both static and instance methods. 가장 유용한 예 중 하나는 String입니다.One of the most useful examples is String:

using static System.String;

참고

static using 문에서는 정규화된 클래스 이름 System.String을 사용해야 합니다.You must use the fully qualified class name, System.String in a static using statement. 대신 string 키워드를 사용할 수 없습니다.You cannot use the string keyword instead.

static using 문에서 가져온 확장 메서드는 확장 메서드 호출 구문을 사용하여 호출될 때만 범위 내에 있습니다.When imported from a static using statement, extension methods are only in scope when called using the extension method invocation syntax. 정적 메서드로 호출될 때는 범위 내에 있지 않습니다.They aren't in scope when called as a static method. 일반적으로 LINQ 쿼리에서 이를 확인할 수 있습니다.You'll often see this in LINQ queries. Enumerable 또는 Queryable을 가져와 LINQ 패턴을 가져올 수 있습니다.You can import the LINQ pattern by importing Enumerable, or Queryable.

using static System.Linq.Enumerable;

일반적으로 확장 메서드는 확장 메서드 호출 식을 사용하여 호출합니다.You typically call extension methods using extension method invocation expressions. 정적 메서드 호출 구문을 사용하여 호출하는 경우는 드물지만 이 경우 클래스 이름을 추가하면 모호함이 해소됩니다.Adding the class name in the rare case where you call them using static method call syntax resolves ambiguity.

static using 지시문은 모든 중첩 형식도 가져옵니다.The static using directive also imports any nested types. 한정 없이 중첩 형식을 참조할 수 있습니다.You can reference any nested types without qualification.

Null 조건 연산자Null-conditional operators

null 조건 연산자를 사용하면 null 확인이 훨씬 더 쉽고 유연합니다.The null conditional operator makes null checks much easier and fluid. 멤버 액세스 .?.로 바꾸세요.Replace the member access . with ?.:

var first = person?.FirstName; 

이전 예제에서 person 개체가 null인 경우 first 변수에 null이 할당됩니다.In the preceding example, the variable first is assigned null if the person object is null. 그렇지 않은 경우 FirstName 속성의 값이 할당됩니다.Otherwise, it is assigned the value of the FirstName property. 가장 중요한 점은 ?.person 변수가 null일 경우 이 코드 줄이 NullReferenceException을 생성하지 않음을 의미한다는 것입니다.Most importantly, the ?. means that this line of code doesn't generate a NullReferenceException if the person variable is null. 대신 이 코드 줄은 단락되고 null을 반환합니다.Instead, it short-circuits and returns null. 또한 배열 또는 인덱서 액세스에 null 조건 연산자를 사용할 수도 있습니다.You can also use a null conditional operator for array or indexer access. 인덱스 식에서 []?[]로 바꾸세요.Replace [] with ?[] in the index expression.

다음 식은 person 값에 관계없이 string을 반환합니다.The following expression returns a string, regardless of the value of person. 이 구문을 null 결합 연산자와 함께 사용하여 속성 중 하나가 null일 경우 기본값을 할당합니다.You often use this construct with the null coalescing operator to assign default values when one of the properties is null. 식이 단락되면 반환된 null 값은 전체 식과 일치하도록 형식화됩니다.When the expression short-circuits, the null value returned is typed to match the full expression.

first = person?.FirstName ?? "Unspecified";

또한 ?.를 사용하여 메서드를 조건부로 호출할 수도 있습니다.You can also use ?. to conditionally invoke methods. null 조건 연산자와 함께 멤버 함수를 사용하는 가장 일반적인 경우는 null일 수 있는 대리자(또는 이벤트 처리기)를 안전하게 호출하는 것입니다.The most common use of member functions with the null conditional operator is to safely invoke delegates (or event handlers) that may be null. ?. 연산자를 통해 대리자의 Invoke 메서드를 호출하여 멤버에 액세스합니다.You'll call the delegate's Invoke method using the ?. operator to access the member. 대리자 패턴 문서에서 예제를 볼 수 있습니다.You can see an example in the delegate patterns article.

?. 연산자 규칙을 적용하면 연산자의 왼쪽이 한 번만 계산됩니다.The rules of the ?. operator ensure that the left-hand side of the operator is evaluated only once. 이렇게 하면 이벤트 처리기 사용 예제를 비롯한 많은 관용구를 구현할 수 있습니다.It enables many idioms, including the following example using event handlers:

// preferred in C# 6:
this.SomethingHappened?.Invoke(this, eventArgs);

또한 왼쪽이 한 번만 계산되도록 하면 ?. 왼쪽에서 메서드 호출을 포함한 모든 식을 사용할 수 있습니다.Ensuring that the left side is evaluated only once also enables you to use any expression, including method calls, on the left side of the ?.

문자열 보간String interpolation

C# 6에서는 새 문자열 보간 기능을 통해 문자열에 식을 포함할 수 있습니다.With C# 6, the new string interpolation feature enables you to embed expressions in a string. 문자열 앞에 $를 추가하고 {} 사이에 서수 대신 식을 사용하기만 하면 됩니다.Simply preface the string with $and use expressions between { and } instead of ordinals:

public string FullName => $"{FirstName} {LastName}";

이 예제에서는 대체 식에 속성을 사용합니다.This example uses properties for the substituted expressions. 모든 식을 사용할 수 있습니다.You can use any expression. 예를 들어 보간의 일부로 학생의 성적 점수 평균을 계산할 수 있습니다.For example, you could compute a student's grade point average as part of the interpolation:

public string GetGradePointPercentage() =>
    $"Name: {LastName}, {FirstName}. G.P.A: {Grades.Average():F2}";

이전 코드 줄은 소수 자릿수가 두 개인 부동 소수점 숫자로 Grades.Average() 값의 형식을 지정합니다.The preceding line of code formats the value for Grades.Average() as a floating-point number with two decimal places.

일반적으로 특정 문화권을 사용하여 생성된 문자열의 서식을 지정해야 할 수 있습니다.Often, you may need to format the string produced using a specific culture. 문자열 보간에 의해 생성된 개체가 암시적으로 System.FormattableString으로 변환될 수 있다는 사실을 참고하세요.You use the fact that the object produced by a string interpolation can be implicitly converted to System.FormattableString. FormattableString 인스턴스에는 복합 형식 문자열 및 문자열로 변환하기 전에 식을 계산한 결과가 포함됩니다.The FormattableString instance contains the composite format string and the results of evaluating the expressions before converting them to strings. 문자열 형식을 지정할 때 FormattableString.ToString(IFormatProvider) 메서드를 사용하여 문화권을 지정합니다.Use the FormattableString.ToString(IFormatProvider) method to specify the culture when formatting a string. 다음 예에서는 독일(de-DE) 문화권을 사용하여 문자열을 생성합니다.The following example produces a string using the German (de-DE) culture. (기본적으로 독일 문화권은 ',' 문자를 소수 구분 기호로 사용하고 '.' 문자를 천 단위 구분 기호로 사용합니다.)(By default, the German culture uses the ',' character for the decimal separator, and the '.' character as the thousands separator.)

FormattableString str = $"Average grade is {s.Grades.Average()}";
var gradeStr = str.ToString(new System.Globalization.CultureInfo("de-DE"));

문자열 보간을 시작하려면 C#의 문자열 보간 대화형 자습서, 문자열 보간 문서 및 C#의 문자열 보간 자습서를 참조하세요.To get started with string interpolation, see the String interpolation in C# interactive tutorial, the String interpolation article, and the String interpolation in C# tutorial.

예외 필터Exception filters

예외 필터는 지정된 catch 절을 적용해야 하는 경우를 결정하는 절입니다.Exception Filters are clauses that determine when a given catch clause should be applied. 예외 필터에 사용된 식이 true로 계산되면 catch 절은 예외에 대한 일반적인 처리를 수행합니다.If the expression used for an exception filter evaluates to true, the catch clause performs its normal processing on an exception. 식이 false로 계산되면 catch 절을 건너뜁니다.If the expression evaluates to false, then the catch clause is skipped. 한 가지 사용 예는 예외에 대한 정보를 검사하여 catch 절이 예외를 처리할 수 있는지 결정하는 것입니다.One use is to examine information about an exception to determine if a catch clause can process the exception:

public static async Task<string> MakeRequest()
{
    WebRequestHandler webRequestHandler = new WebRequestHandler();
    webRequestHandler.AllowAutoRedirect = false;
    using (HttpClient client = new HttpClient(webRequestHandler))
    {
        var stringTask = client.GetStringAsync("https://docs.microsoft.com/en-us/dotnet/about/");
        try
        {
            var responseText = await stringTask;
            return responseText;
        }
        catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
        {
            return "Site Moved";
        }
    }
}

nameofThe nameof expression

nameof 식은 기호 이름으로 계산됩니다.The nameof expression evaluates to the name of a symbol. 변수, 속성 또는 멤버 필드의 이름이 필요할 때마다 도구를 작동하는 것이 좋습니다.It's a great way to get tools working whenever you need the name of a variable, a property, or a member field. nameof의 가장 일반적인 사용 예 중 하나는 예외를 일으킨 기호의 이름을 제공하는 것입니다.One of the most common uses for nameof is to provide the name of a symbol that caused an exception:

if (IsNullOrWhiteSpace(lastName))
    throw new ArgumentException(message: "Cannot be blank", paramName: nameof(lastName));

또 다른 사용 예는 INotifyPropertyChanged 인터페이스를 구현하는 XAML 기반 애플리케이션에서 사용하는 것입니다.Another use is with XAML-based applications that implement the INotifyPropertyChanged interface:

public string LastName
{
    get { return lastName; }
    set
    {
        if (value != lastName)
        {
            lastName = value;
            PropertyChanged?.Invoke(this, 
                new PropertyChangedEventArgs(nameof(LastName)));
        }
    }
}
private string lastName;

Catch 및 Finally 블록의 AwaitAwait in Catch and Finally blocks

C# 5에는 await 식을 배치할 수 있는 위치에 대한 여러 제한 사항이 있었습니다.C# 5 had several limitations around where you could place await expressions. 이제 C# 6에서는 catch 또는 finally 식에서 await를 사용할 수 있습니다.With C# 6, you can now use await in catch or finally expressions. 이 방법이 로깅 시나리오에서 가장 일반적을 사용됩니다.This is most often used with logging scenarios:

public static async Task<string> MakeRequestAndLogFailures()
{ 
    await logMethodEntrance();
    var client = new System.Net.Http.HttpClient();
    var streamTask = client.GetStringAsync("https://localHost:10000");
    try {
        var responseText = await streamTask;
        return responseText;
    } catch (System.Net.Http.HttpRequestException e) when (e.Message.Contains("301"))
    {
        await logError("Recovered from redirect", e);
        return "Site Moved";
    }
    finally
    {
        await logMethodExit();
        client.Dispose();
    }
}

catchfinally 절 내부에 await 지원을 추가하기 위한 구현 세부 정보에 따라 동작이 동기 코드에 대한 동작과 일치합니다.The implementation details for adding await support inside catch and finally clauses ensure that the behavior is consistent with the behavior for synchronous code. catch 또는 finally 절에서 실행되는 코드가 throw되면 실행은 다음 바깥쪽 블록에서 적합한 catch 절을 검색합니다.When code executed in a catch or finally clause throws, execution looks for a suitable catch clause in the next surrounding block. 현재 예외가 있는 경우 해당 예외가 손실됩니다.If there was a current exception, that exception is lost. catchfinally 절에서 대기된 식에서도 같은 작업이 수행됩니다. 적합한 catch가 검색되고 현재 예외(있는 경우)가 손실됩니다.The same happens with awaited expressions in catch and finally clauses: a suitable catch is searched for, and the current exception, if any, is lost.

참고

이 동작 때문에 새 예외가 추가되지 않도록 catchfinally 절을 신중하게 작성하는 것이 좋습니다.This behavior is the reason it's recommended to write catch and finally clauses carefully, to avoid introducing new exceptions.

인덱서를 사용하여 연결 컬렉션 초기화Initialize associative collections using indexers

인덱스 이니셜라이저는 컬렉션 이니셜라이저를 인덱스 사용과 더 일관되도록 하는 두 가지 기능 중 하나입니다.Index Initializers is one of two features that make collection initializers more consistent with index usage. C#의 이전 릴리스에서는 키 및 값 쌍을 중괄호로 묶어 Dictionary<TKey,TValue>를 포함하여 시퀀스 스타일 컬렉션에서 컬렉션 이니셜라이저를 사용할 수 있습니다.In earlier releases of C#, you could use collection initializers with sequence style collections, including Dictionary<TKey,TValue>, by adding braces around key and value pairs:

private Dictionary<int, string> messages = new Dictionary<int, string>
{
    { 404, "Page not Found"},
    { 302, "Page moved, but left a forwarding address."},
    { 500, "The web server can't come out to play today."}
};

Dictionary<TKey,TValue> 컬렉션 및 액세스 가능한 Add 메서드가 둘 이상의 인수를 허용하는 다른 형식에서 이를 사용할 수 있습니다.You can use them with Dictionary<TKey,TValue> collections and other types where the accessible Add method accepts more than one argument. 새로운 구문은 컬렉션에 인덱스를 사용하여 할당을 지원합니다.The new syntax supports assignment using an index into the collection:

private Dictionary<int, string> webErrors = new Dictionary<int, string>
{
    [404] = "Page not Found",
    [302] = "Page moved, but left a forwarding address.",
    [500] = "The web server can't come out to play today."
};

이 기능은 여러 버전에 대한 시퀀스 컨테이너를 대신한 것과 비슷한 구문을 사용하여 연관 컨테이너를 초기화할 수 있음을 의미합니다.This feature means that associative containers can be initialized using syntax similar to what's been in place for sequence containers for several versions.

컬렉션 이니셜라이저의 확장 Add 메서드Extension Add methods in collection initializers

컬렉션을 더 쉽게 초기화하도록 하는 또 다른 기능은 Add 메서드에 확장 메서드를 사용하는 기능입니다.Another feature that makes collection initialization easier is the ability to use an extension method for the Add method. 이 기능은 Visual Basic의 패리티를 위해 추가되었습니다.This feature was added for parity with Visual Basic. 이 기능은 의미상으로 새 항목을 추가하기 위해 다른 이름을 가진 메서드가 포함된 사용자 지정 컬렉션 클래스가 있는 경우 가장 유용합니다.The feature is most useful when you have a custom collection class that has a method with a different name to semantically add new items.

향상된 오버로드 확인Improved overload resolution

이 마지막 기능은 알지 못하는 기능일 수 있습니다.This last feature is one you probably won't notice. C# 컴파일러의 이전 버전에서 람다 식을 포함하는 일부 메서드 호출이 모호한 것으로 확인될 수 있는 구문이 있었습니다.There were constructs where the previous version of the C# compiler may have found some method calls involving lambda expressions ambiguous. 이 메서드를 살펴봅니다.Consider this method:

static Task DoThings() 
{
     return Task.FromResult(0); 
}

C#의 이전 버전에서는 메서드 그룹 구문을 통한 해당 메서드 호출이 실패합니다.In earlier versions of C#, calling that method using the method group syntax would fail:

Task.Run(DoThings); 

이전 컴파일러에서는 Task.Run(Action)Task.Run(Func<Task>())를 제대로 구분할 수 없습니다.The earlier compiler couldn't distinguish correctly between Task.Run(Action) and Task.Run(Func<Task>()). 이전 버전에서는 람다 식을 인수로 사용해야 했습니다.In previous versions, you'd need to use a lambda expression as an argument:

Task.Run(() => DoThings());

C# 6 컴파일러에서는 Task.Run(Func<Task>())가 더 나은 선택인지 제대로 결정합니다.The C# 6 compiler correctly determines that Task.Run(Func<Task>()) is a better choice.

deterministic 컴파일러 출력Deterministic compiler output

-deterministic 옵션은 컴파일러가 동일한 원본 파일의 연속적인 컴파일을 위해 바이트 단위의 동일한 출력 어셈블리를 생성하도록 지시합니다.The -deterministic option instructs the compiler to produce a byte-for-byte identical output assembly for successive compilations of the same source files.

기본적으로 컴파일이 실행될 때마다 고유한 출력이 생성됩니다.By default, every compilation produces unique output on each compilation. 컴파일러는 임의의 숫자에서 생성된 GUID와 타임스탬프를 추가합니다.The compiler adds a timestamp, and a GUID generated from random numbers. 바이트별 출력을 비교하여 빌드 간 일관성을 유지하려면 이 옵션을 사용합니다.You use this option if you want to compare the byte-for-byte output to ensure consistency across builds.

자세한 내용은 -deterministic 컴파일러 옵션 문서를 참조하세요.For more information, see the -deterministic compiler option article.