본문 바로가기

공부/개발독서 - c++, c#

[effective C#] 1장 C# 언어 요소 (아이템 5 ~ 7)

반응형

아이템5: 문화권별로 다른 문자열을 생성하려면 FormattableString을 사용하라

아이템4 에서 다룬 문자열 보간 기능의 결과로 생성되는 반환값은 문자열일 수도 있지만 FormattableString을 상속한 타입일 수도 있다. 다음 코드는 FormattableString 타입의 객체를 이용하여 문화권과 언어를 지정하여 문자열을 생성하는 방법을 나타낸다.

public static string ToGerman(FormattableString src) =>
    string.Format(null,
        System.Globalization.CultureInfo.CreateSpecificCulture("de-de"),
        src.Format, src.GetArguments());
public static string ToFrench(FormattableString src) =>
    string.Format(null,
        System.Globalization.CultureInfo.CreateSpecificCulture("fr-CA"),
        src.Format, src.GetArguments());

아이템6: nameof() 연산자를 적극 활용하라

이질적인 플랫폼 사이에서 데이터를 주고 받 는 경우, 이름이나 문자열 식별자에 의존하는 간단한 라이브러리가 사용되는 일이 많다. 이 같은 방식이 간편할지는 몰라도 그에 준하는 추가 비용이 발생한다는 것을 잊어서는 안된다. 대표적인 단점이 바로 타입 정보를 손실한다는 것이다.

C# 언어 설계팀은 이러한 문제를 인지하고 C# 6.0에 nameof()라는 연산자를 추가했다. 이 키워드의 역할은 심볼 그 자체를 해당 심볼을 포함하는 문자열(로컬 네임)로 대체해준다.

public string Name
{
    get => name;
    set
    {
        if(value != name)
        {
            name = value;
            PropertyChange?.Invoke(this,
                new PropertyChangedEvent(nameof(Name)));
        }
    }
}

private string name;

nameof() 연산자를 사용했기 때문에 속성의 이름을 변경할 경우 이벤트의 인자로 전달해야 하는 문자열도 쉽게 변경할 수 있다. nameof()는 심볼의 이름을 평가하여 타입, 변수, 인터페이스, 네임스페이스에 대하여 사용 할 수 있다. 다만 제네릭 타입을 사용할 경우 부분적으로 제약이 있어 모든 타입 매개변수를 지정한 닫힌 제너릭 타입만을 사용할 수 있다.

문자열을 하드코딩하는 대신 nameof 연산자를 이용하면 실수를 줄일 수 있다. 심볼의 이름을 완전히 바꾸거나 수정할 경우에도 손쉽게 그 변경 사항을 반영할 수 있다.

아이템7: 델리게이트를 이용하여 콜백을 표현하라

콜백은 서버가 클라이언트에게 비동기적으로 피드백을 주기 위해서 주로 사용하는 방법이다. 이를 위해 멀티스레딩 기술도 사용되고, 동기적으로 상태를 갱신하는 기법도 활용된다. 콜백은 C#에서 델리게이트를 이용하여 표현된다.

델리게이트를 활용하면 타입 안정적인 콜백을 정의할 수 있다. 대부분의 경우 event와 함께 사용되지만 반드시 그래야 하는 것은 아니다. .NET Framewordk 라이브러리는 Predicate<T>, Action<>, Function<>와 같은 형태로 자주 사용되는 델리게이트를 정의해 두고 있다.

List<int> numbers = Enumerable.Range(1, 200).ToList();

var oddNumbers = numbers.Find(n => n % 2 == 1);
var test = numbers.TrueForAll(n => n < 50);

numbers.RemoveAll(n => n % 2 == 0);
numbers.ForEach(item => Console.WriteLine(item));

LINQ는 이러한 개념을 기반으로 만들어졌다. Find() 메서드는 Predicate<int> 형식의 델리게이트를 사용하여 리스트 내에 포함된 요쇼에 대하여 테스트를 수행한다. TrueForAll() 메서드는 각 요소를 개별적으로 테스트하되 모든 항목이 테스트를 통과한 경우 true를 반환한다.

멀티캐스트 델리게이트의 경우 두 가지 주의해야 할 부분이 있다. 먼저 예외에 안전하지 않고, 마지막으로 호출된 대상 함수의 반환값이 델리게이트의 반환값으로 간주된다. 델리게이트는 어떤 예외도 잡지(catch) 않으며, 예외가 발생하면 함수 호출 과정이 중단된다. 이러한 문제를 해결하려면 델리게이트에 포함된 호출 대상 콜백 함수를 직접 다뤄야 한다.

public void LengthyOperation(Func<bool> pred)
{
    bool bContinue = true;
    foreach(ComplicatedClass cl in container)
    {
        cl.DoLengthyOperation();
        foreach (Func<bool> pr in pred.GetInvocationList())
            bContinue &= pr();

        if (!bContinue)
            return;
    }
}

이와 같이 작성하면 델리게이트에 추가된 개별 메서드가 true를 반환한 경우에만 다음 메서드에 대한 호출을 이어간다.

반응형