C# 6 새로운 기능 개요

C# 언어 버전 6은 상용구 코드가 줄어들고 명확성이 향상되며 일관성이 향상되도록 언어를 계속 발전시키고 있습니다. 클리너 초기화 구문, catch/finally 블록에서 await를 사용하는 기능 및 null 조건부 ? 연산자는 특히 유용합니다.

참고 항목

최신 버전의 C# 언어 버전 7에 대한 자세한 내용은 C# 7.0의 새로운 기능 문서를 참조하세요.

이 문서에서는 C# 6의 새로운 기능을 소개합니다. 모노 컴파일러에서 완벽하게 지원되며 개발자는 모든 Xamarin 대상 플랫폼에서 새로운 기능을 사용할 수 있습니다.

C# 6 비디오의 새로운 기능

C# 6 사용

C# 6 컴파일러는 모든 최신 버전의 Mac용 Visual Studio 사용됩니다. 명령줄 컴파일러를 사용하는 경우 4.0 이상을 반환하는지 mcs --version 확인해야 합니다. Mac용 Visual Studio 사용자는 정보 Mac용 Visual Studio Mac용 Visual Studio 세부 정보 표시를 > 참조하여 Mono 4 이상 버전이 설치된 경우 검사 > 수 있습니다.

상용구 감소

using static

열거형 및 같은 특정 클래스 System.Math는 주로 정적 값 및 함수의 소유자입니다. C# 6에서는 단일 using static 문을 사용하여 형식의 모든 정적 멤버를 가져올 수 있습니다. C# 5 및 C# 6의 일반적인 삼각 함수를 비교합니다.

// Classic C#
class MyClass
{
    public static Tuple<double,double> SolarAngleOld(double latitude, double declination, double hourAngle)
    {
        var tmp = Math.Sin (latitude) * Math.Sin (declination) + Math.Cos (latitude) * Math.Cos (declination) * Math.Cos (hourAngle);
        return Tuple.Create (Math.Asin (tmp), Math.Acos (tmp));
    }
}

// C# 6
using static System.Math;

class MyClass
{
    public static Tuple<double, double> SolarAngleNew(double latitude, double declination, double hourAngle)
    {
        var tmp = Asin (latitude) * Sin (declination) + Cos (latitude) * Cos (declination) * Cos (hourAngle);
        return Tuple.Create (Asin (tmp), Acos (tmp));
    }
}

using static 에서는 공용 const 필드(예: Math.PIMath.E직접 액세스할 수 있음)를 만들지 않습니다.

for (var angle = 0.0; angle <= Math.PI * 2.0; angle += Math.PI / 8) ... 
//PI is const, not static, so requires Math.PI

확장 메서드에서 정적 사용

using static 시설은 확장 메서드와 약간 다르게 작동합니다. 확장 메서드는 사용하여 static작성되지만 작동할 인스턴스가 없으면 의미가 없습니다. using static 따라서 확장 메서드를 정의하는 형식과 함께 사용하면 확장 메서드를 대상 형식(메서드의 this 형식)에서 사용할 수 있게 됩니다. 예를 들어 using static System.Linq.Enumerable 모든 LINQ 형식을 가져오지 않고 개체의 IEnumerable<T> API를 확장하는 데 사용할 수 있습니다.

using static System.Linq.Enumerable;
using static System.String;

class Program
{
    static void Main()
    {
        var values = new int[] { 1, 2, 3, 4 };
        var evenValues = values.Where (i => i % 2 == 0);
        System.Console.WriteLine (Join(",", evenValues));
    }
}

이전 예제에서는 동작의 차이를 보여 줍니다. 확장 메서드 Enumerable.Where 는 배열과 연결되지만 정적 메서드 String.Join 는 형식에 String 대한 참조 없이 호출할 수 있습니다.

nameof 식

경우에 따라 변수 또는 필드를 지정한 이름을 참조하려고 합니다. C# 6 nameof(someVariableOrFieldOrType) 에서 문자열 "someVariableOrFieldOrType"을 반환합니다. 예를 들어, throw할 ArgumentException 때 잘못된 인수의 이름을 지정할 가능성이 매우 높습니다.

throw new ArgumentException ("Problem with " + nameof(myInvalidArgument))

식의 nameof 주요 이점은 형식 검사 도구 기반 리팩터링과 호환된다는 것입니다. 식의 nameof 형식 검사 지정은 형식을 동적으로 연결하는 데 사용되는 경우에 string 특히 환영합니다. 예를 들어 iOS에서는 개체를 string 프로토타입 UITableViewCellUITableView하는 데 사용되는 형식을 지정하는 데 사용됩니다. nameof 에서는 맞춤법 오류 또는 잘못된 리팩터링으로 인해 이 연결이 실패하지 않도록 할 수 있습니다.

public override UITableViewCell GetCell (UITableView tableView, NSIndexPath indexPath)
{
    var cell = tableView.DequeueReusableCell (nameof(CellTypeA), indexPath);
    cell.TextLabel.Text = objects [indexPath.Row].ToString ();
    return cell;
}

정규화된 이름을 nameof전달할 수 있지만 마지막 요소(마지막 .이후)만 반환됩니다. 예를 들어 Xamarin.Forms에서 데이터 바인딩을 추가할 수 있습니다.

var myReactiveInstance = new ReactiveType ();
var myLabelOld.BindingContext = myReactiveInstance;
var myLabelNew.BindingContext = myReactiveInstance;
var myLabelOld.SetBinding (Label.TextProperty, "StringField");
var myLabelNew.SetBinding (Label.TextProperty, nameof(ReactiveType.StringField));

두 호출 SetBinding 은 동일한 값을 nameof(ReactiveType.StringField)"StringField"전달합니다. 즉, 처음에 예상한 대로 전달되지 않습니다 "ReactiveType.StringField" .

Null 조건부 연산자

C#에 대한 이전 업데이트에서는 nullable 값을 처리할 때 상용구 코드의 양을 줄이기 위해 null 허용 형식 및 null 병합 연산 ?? 자의 개념을 도입했습니다. C# 6은 "null 조건부 연산자" ?.를 사용하여 이 테마를 계속합니다. 식의 오른쪽에 있는 개체에 사용할 경우 null 조건부 연산자는 개체가 아닌 경우 멤버 값을 반환하고 null 그렇지 않으면 null 다음을 반환합니다.

var ss = new string[] { "Foo", null };
var length0 = ss [0]?.Length; // 3
var length1 = ss [1]?.Length; // null
var lengths = ss.Select (s => s?.Length ?? 0); //[3, 0]

(둘 다 length0 형식 length1 으로 int?유추됨)

이전 예제의 마지막 줄은 null 병합 연산자를 조합하여 ?? null 조건부 연산자를 보여줍니다?. 새 C# 6 null 조건부 연산자는 배열의 두 번째 요소를 반환 null 하며, 이때 null 병합 연산자가 시작되어 배열에 0 lengths 을 제공합니다(적절한지 여부와 관계없이 문제별).

null 조건부 연산자는 많은 애플리케이션에서 필요한 상용구 null 검사 양을 크게 줄여야 합니다.

모호성으로 인해 null 조건부 연산자에 몇 가지 제한 사항이 있습니다. 대리자를 ? 사용할 수 있으므로 괄호로 괄호가 있는 인수 목록을 즉시 따를 수 없습니다.

SomeDelegate?("Some Argument") // Not allowed

그러나 Invoke 인수 목록에서 분리 ? 하는 데 사용할 수 있으며 상용구의 -검사 블록에 비해 null여전히 현저한 개선 사항입니다.

public event EventHandler HandoffOccurred;
public override bool ContinueUserActivity (UIApplication application, NSUserActivity userActivity, UIApplicationRestorationHandler completionHandler)
{
    HandoffOccurred?.Invoke (this, userActivity.UserInfo);
    return true;
}

문자열 보간

String.Format 함수는 일반적으로 형식 문자열(예: String.Format("Expected: {0} Received: {1}.", expected, received)의 자리 표시자로 인덱스를 사용했습니다. 물론 새 값을 추가하면 항상 인수를 계산하고, 자리 표시자를 다시 매기고, 인수 목록의 오른쪽 시퀀스에 새 인수를 삽입하는 성가신 작은 작업이 포함되었습니다.

C# 6의 새 문자열 보간 기능이 크게 향상되었습니다 String.Format. 이제 접두 $사로 된 문자열에서 변수의 이름을 직접 지정할 수 있습니다. 예를 들면 다음과 같습니다.

$"Expected: {expected} Received: {received}."

물론 변수는 검사 철자가 잘못되었거나 사용할 수 없는 변수로 인해 컴파일러 오류가 발생합니다.

자리 표시자는 단순 변수일 필요가 없으며 식일 수 있습니다. 이러한 자리 표시자 내에서 따옴표를 이스케이프하지 않고 따옴표를 사용할 수 있습니다. 예를 들어 다음 사항에 유의 "s" 하세요.

var s = $"Timestamp: {DateTime.Now.ToString ("s", System.Globalization.CultureInfo.InvariantCulture )}"

문자열 보간은 .의 맞춤 및 서식 지정 구문을 String.Format지원합니다. 이전에 작성한 {index, alignment:format}것처럼 C# 6에서는 다음을 작성 {placeholder, alignment:format}합니다.

using static System.Linq.Enumerable;
using System;

class Program
{
    static void Main ()
    {
        var values = new int[] { 1, 2, 3, 4, 12, 123456 };
        foreach (var s in values.Select (i => $"The value is { i,10:N2}.")) {
            Console.WriteLine (s);
        }
    Console.WriteLine ($"Minimum is { values.Min(i => i):N2}.");
    }
}

결과는 다음과 같습니다.

The value is       1.00.
The value is       2.00.
The value is       3.00.
The value is       4.00.
The value is      12.00.
The value is 123,456.00.
Minimum is 1.00.

문자열 보간은 문자열 리터럴과 함께 @"" 사용할 수 없으며 자리 표시자가 사용되지 않더라도 호환const되지 않는 구문 설탕String.Format입니다.

const string s = $"Foo"; //Error : const requires value

문자열 보간을 사용하여 함수 인수를 작성하는 일반적인 사용 사례에서는 이스케이프, 인코딩 및 문화권 문제에 주의해야 합니다. 물론 SQL 및 URL 쿼리는 삭제하는 데 중요합니다. 마찬가지로 String.Format문자열 보간은 .를 CultureInfo.CurrentCulture사용합니다. 사용 CultureInfo.InvariantCulture 은 좀 더 많은 단어입니다.

Thread.CurrentThread.CurrentCulture  = new CultureInfo ("de");
Console.WriteLine ($"Today is: {DateTime.Now}"); //"21.05.2015 13:52:51"
Console.WriteLine ($"Today is: {DateTime.Now.ToString(CultureInfo.InvariantCulture)}"); //"05/21/2015 13:52:51"

초기화

C# 6은 속성, 필드 및 멤버를 지정하는 여러 가지 간결한 방법을 제공합니다.

자동 속성 초기화

이제 필드와 동일한 간결한 방식으로 자동 속성을 초기화할 수 있습니다. 변경할 수 없는 자동 속성은 getter로만 작성할 수 있습니다.

class ToDo
{
    public DateTime Due { get; set; } = DateTime.Now.AddDays(1);
    public DateTime Created { get; } = DateTime.Now;

생성자에서 getter 전용 자동 속성의 값을 설정할 수 있습니다.

class ToDo
{
    public DateTime Due { get; set; } = DateTime.Now.AddDays(1);
    public DateTime Created { get; } = DateTime.Now;
    public string Description { get; }

    public ToDo (string description)
    {
        this.Description = description; //Can assign (only in constructor!)
    }

자동 속성의 초기화는 일반적인 공간 절약 기능이며 개체의 불변성을 강조하려는 개발자에게 도움이 됩니다.

인덱스 이니셜라이저

C# 6에는 인덱서가 있는 형식에서 키와 값을 모두 설정할 수 있는 인덱스 이니셜라이저가 도입되었습니다. 일반적으로 -style 데이터 구조에 대한 Dictionary것입니다.

partial void ActivateHandoffClicked (WatchKit.WKInterfaceButton sender)
{
    var userInfo = new NSMutableDictionary {
        ["Created"] = NSDate.Now,
        ["Due"] = NSDate.Now.AddSeconds(60 * 60 * 24),
        ["Task"] = Description
    };
    UpdateUserActivity ("com.xamarin.ToDo.edit", userInfo, null);
    statusLabel.SetText ("Check phone");
}

식 본문 함수 멤버

람다 함수에는 몇 가지 이점이 있으며, 그 중 하나는 단순히 공간을 절약하는 것입니다. 마찬가지로 식 본문 클래스 멤버를 사용하면 이전 버전의 C# 6보다 작은 함수를 좀 더 간결하게 표현할 수 있습니다.

식 본문 함수 멤버는 기존 블록 구문 대신 람다 화살표 구문을 사용합니다.

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

람다 화살표 구문은 명시적 return구문을 사용하지 않습니다. 반환 void하는 함수의 경우 식도 문이어야 합니다.

public void Log(string message) => System.Console.WriteLine($"{DateTime.Now.ToString ("s", System.Globalization.CultureInfo.InvariantCulture )}: {message}");

식 본문 멤버는 메서드에 대해 지원되지만 속성은 지원하지 않는 async 규칙의 적용을 받습니다.

//A method, so async is valid
public async Task DelayInSeconds(int seconds) => await Task.Delay(seconds * 1000);
//The following property will not compile
public async Task<int> LeisureHours => await Task.FromResult<char> (DateTime.Now.DayOfWeek.ToString().First()) == 'S' ? 16 : 5;

예외

예외 처리는 제대로 하기 어렵다는 두 가지 방법이 없습니다. C# 6의 새로운 기능은 예외 처리를 보다 유연하고 일관되게 만듭니다.

예외 필터

정의상 예외는 비정상적인 상황에서 발생하며 특정 형식의 예외가 발생할 수 있는 모든 방법에 대해 추론하고 코딩하기가 매우 어려울 수 있습니다. C# 6에서는 런타임 평가 필터를 사용하여 실행 처리기를 보호 하는 기능을 도입 합니다. 이 작업은 일반 catch(ExceptionType) 선언 다음에 패턴을 추가하여 when (bool) 수행됩니다. 다음에서 필터는 다른 구문 분석 오류와는 달리 매개 변수와 관련된 date 구문 분석 오류를 구분합니다.

public void ExceptionFilters(string aFloat, string date, string anInt)
{
    try
    {
        var f = Double.Parse(aFloat);
        var d = DateTime.Parse(date);
        var n = Int32.Parse(anInt);
    } catch (FormatException e) when (e.Message.IndexOf("DateTime") > -1) {
        Console.WriteLine ($"Problem parsing \"{nameof(date)}\" argument");
    } catch (FormatException x) {
        Console.WriteLine ("Problem parsing some other argument");
    }
}

catch에서 대기 중... 마침내...

async C# 5에서 도입된 기능은 언어에 대한 게임 체인저였습니다. C# 5 await 에서는 기능의 값을 고려할 때 성가신 인 및 finally 블록이 async/await 허용되지 catch 않았습니다. C# 6은 이 제한을 제거하므로 다음 코드 조각과 같이 프로그램을 통해 비동기 결과를 일관되게 대기할 수 있습니다.

async void SomeMethod()
{
    try {
        //...etc...
    } catch (Exception x) {
        var diagnosticData = await GenerateDiagnosticsAsync (x);
        Logger.log (diagnosticData);
    } finally {
        await someObject.FinalizeAsync ();
    }
}

요약

C# 언어는 개발자의 생산성을 높이는 동시에 모범 사례를 홍보하고 도구를 지원하기 위해 계속 진화하고 있습니다. 이 문서에서는 C# 6의 새로운 언어 기능에 대한 개요를 제공했으며 사용 방법을 간략하게 설명했습니다.