오류 없는 C# 파트 II: 기능적 데이터 처리

11696 단어 functionalcsharp
가변성 버그와 스레드 불안전성은 데이터 처리에서 큰 문제입니다. 다행히 .NET Framework는 변경할 수 없는 컬렉션을 강력하게 지원하여 전체 범주의 버그를 제거합니다. 이 게시물에서는 이 시리즈의 이전 게시물에서 만든 extension methods을 기반으로 하여 IMaybe monad type을 사용하여 C#의 목록과 상호 작용하는 더 안전한 방법을 만드는 방법을 보여줍니다.

전제 조건: System.Collections.Immutable



이 게시물은 C#의 멋진 Systems.Collections.Immutable 라이브러리를 기반으로 합니다. 불변 컬렉션의 개념을 칭찬하기 위해 이미 많은 디지털 잉크가 쏟아졌고 추가 할 것이 없지만 아직 익숙하지 않은 경우이 detailed post on the benefits thereof을 읽는 것이 좋습니다.

이 게시물은 확장 방법도 사용합니다. 이미 확장 방법에 익숙할 필요는 없지만 보완이 필요하다면 this Microsoft article을 추천합니다.

데이터 처리에 대한 안전성의 필요성



C#의 불변 컬렉션은 함수형 프로그래밍 여정에서 우리를 잘 이끌지만 여백에서 우리를 거의 보호하지 못합니다. 빈 목록의 경우 우리는 어떻게 자신을 보호합니까? 두려운 ArgumentOutOfRangeException을 처리하기에 충분한 try/catch 블록이 있는지 어떻게 확신할 수 있습니까?

이러한 문제는 avoidable만큼 NullReferenceException이며 약간의 확장 메서드 마술로 우리는 곧 이러한 문제를 잊게 될 것입니다.

확장 메서드 클래스 설정



새 클래스를 인스턴스화하여 시작합니다. 확장 메서드는 정적 클래스에 있어야 하며, 규칙에 따라 해당 정적 클래스는 "Extensions"로 끝나야 합니다. 내 EnumerableExtensions를 호출하고 이를 광범위한 IEnumerable<T> 인터페이스(이 중 IImmutableList<T>이 구현됨)에 적용하겠습니다.

EnumerableExtensions.cs

public static class EnumerableExtensions
{
}

확장 메서드 서명



다음으로 확장 메서드에 대한 서명을 작성해 보겠습니다. 확장 메서드 서명은 매개 변수 목록에서 this 키워드를 사용하므로 정적 메서드 대신 일반 개체 메서드로 사용할 수 있습니다.

public static class EnumerableExtensions
{ 
    public static IMaybe<T> MaybeFirst<T>(this IEnumerable<T> xs)
        => throw new NotImplementedException(); 

    public static IMaybe<T> MaybeLast<T>(this IEnumerable<T> xs)
        => throw new NotImplementedException();
}

메서드를 래핑하는 가장 안전한 방법



이제 메서드를 구현하고 여기에서 검사를 수행하여 안전하게 모나드를 래핑하도록 하겠습니다.

public static class EnumerableExtensions
{ 
    public static IMaybe<T> MaybeFirst<T>(this IEnumerable<T> xs)
        => xs.Any() 
        ? Maybe.Factory<T>(xs.First()) 
        : new None<T>(); 

    public static IMaybe<T> MaybeLast<T>(this IEnumerable<T> xs)
        => xs.Any() ? Maybe.Factory<T>(xs.Last()) : new None<T>();
}

이 두 가지 경우를 처리할 수 있는 몇 가지 방법이 있습니다. Count()을 사용하여 개수가 1보다 큰지 확인할 수 있었지만 IEnumerable이 매우 방대하면 실행하는 데 오랜 시간이 걸릴 수 있습니다. 작업을 try으로 래핑하고 System.InvalidOperationException을 포착할 수도 있지만, 포착하기 위해 예외를 던지는 것은 계산적으로 다소 비용이 많이 듭니다.
Maybe.Factory(...) 을 직접 사용하여 FirstOrDefault() 을 호출할 수도 있지만, null 및 1045067914 와 같은 기본 유형의 경우와 같이 유형의 기본값이 DateTime 이 아닌 경우 예기치 않은 결과가 발생했을 것입니다.
int 은 여기에서 모범 사례입니다. 따라서 "Maybe"확장 방법을 고수하고 Any()First() 을 피하는 한 항상 모범 사례를 사용하고 있다고 확신할 수 있습니다. Last() 을 제거했습니다.

System.ArgumentOutOfRangeException에서 System.InvalidOperationException 제거



여기 있는 동안 ElementAt() 에서 오는 예외를 제거하기 위해 확장 메서드를 하나 더 추가해 보겠습니다. ElementAt()은 요청된 인덱스보다 적은 요소가 있으면 실패합니다.

이 경우 ElementAt()은 도움이 되지 않으며 Any()은 여전히 ​​계산 비용이 많이 들기 때문에 예외를 잡는 것만으로도 충분합니다.

public static class EnumerableExtensions
{ 
    public static IMaybe<T> MaybeElementAt<T>(this IEnumerable<T> xs, int i) 
    { 
        try { return Maybe.Factory<T>(xs.ElementAt(i)); } 
        catch { return new None<T>(); } 
    } 

    public static IMaybe<T> MaybeFirst<T>(this IEnumerable<T> xs)
        => xs.Any() ? Maybe.Factory<T>(xs.First()) : new None<T>(); 

    public static IMaybe<T> MaybeLast<T>(this IEnumerable<T> xs)
        => xs.Any() ? Maybe.Factory<T>(xs.Last()) : new None<T>();
}

다시는 데이터 누락에 대해 걱정하지 마십시오.



확장 방법을 고수하는 한 목록의 특정 요소를 가져오기 위해 가장 안전하고 효율적이며 가장 정확한 방법을 사용하고 있다는 것을 항상 알고 있습니다.

이것으로 IMaybe 모나드에 대한 시리즈의 일부를 마무리하고 누락된 예상 데이터와 관련된 예외를 제거합니다. 내 next post 에서 try 모나드라고 하는 특별한 종류의 모나드를 사용하여 다른 종류의 런타임 예외를 제거하는 작업을 할 것입니다.

좋은 웹페이지 즐겨찾기