2016-06-14 3 views
5

.NET BCL에는 일반적인 bool TryXXX(out T result) 메서드가 많이 있는데 가장 널리 사용되는 것은 아마도 int.TryParse(...)입니다.비동기 TryParse (...) 패턴

비동기TryXXX() 메서드를 구현하고 싶습니다. 분명히 out 매개 변수를 사용할 수 없습니다.

거기에 맞는 패턴이 있습니까?

더 중요한 것은 파일을 다운로드하고 구문 분석해야합니다. 파일이 존재하지 않을 수도 있습니다.

이것은 내가 지금까지 해낸 것입니다 : 나는 다음과 같은 정의를 마련했습니다

public async Task<DownloadResult> TryDownloadAndParse(string fileUri) 
{ 
    try 
    { 
     result = await DownloadAndParse(fileUri); //defined elsewhere 
     return new DownloadResult {IsFound = true, Value = result} 
    } 
    catch (DownloadNotFoundException ex) 
    { 
     return new DownloadResult {IsFound = false, Value = null} 
    } 
    //let any other exception pass 
} 

public struct DownloadResult 
{ 
    public bool IsFound { get; set; } 

    public ParsedFile Value { get; set; } 
} 
+1

이 구문 https://github.com/dotnet/roslyn/issues/347이 구현 될 때까지는 자신의 유형을 반환 할 운명에 처해있는 것 같습니다.적어도 Tuple (또는 KeyValuePair가 아니라 Success 및 Result와 같은 합리적인 속성 이름이있는 더 나은 사용자 정의 유형)을 사용하십시오. – Evk

+1

Thx 링크, 좋은 제안! Tuple <>이 KVP <>보다 어떤 이점이 있습니까? 항상 참/거짓 결과와 실제 페이로드라는 정확히 두 항목을 반환해야합니다. 나는 Tuple <>'('.Item1','.Item2' ...)의 속성 이름을 다소 싫어합니다. 튜플이 가지고있는 것에 완전히 불투명합니다. 'KVP'는별로 좋지는 않지만 (적어도 시나리오에서는),'.Value'는 적어도 어느 정도 의미가 있습니다. –

+0

차이점은 의미입니다. KeyValuePair는 key + value입니다. 당신의 경우에는 아무것도 정말로 중요하지 않습니다. 튜플은 두 가지 (또는 그 이상)의 임의의 값입니다 - 귀하의 경우. 이름이 마음에 들지 않으면 Bool 및 T 속성이 적절한 TryAsyncResult 클래스를 적절한 이름으로 만듭니다. 모든 Try 오퍼레이션에 대해 하나의 클래스 만 필요합니다 (제네릭 때문에). – Evk

답변

3

. defaultValue 매개 변수는 대부분 TryGet 메서드를 오버로드 할 수 있습니다. 일반 제약 조건은 메서드의 서명에 포함되지 않으므로 호출 할 메서드를 결정할 때 메서드를 고유하게 만듭니다. 예를 들어 반환 형식도 서명).

public async Task<T> TryGet<T>(Func<Task<T>> func, T defaultValue = null) where T : class 
{ 
    try 
    { 
     return await func(); 
    } 
    catch (ArgumentException) 
    { 
     return defaultValue; 
    } 
    catch (FormatException) 
    { 
     return defaultValue; 
    } 
    catch (OverflowException) 
    { 
     return defaultValue; 
    } 
} 

public async Task<Nullable<T>> TryGet<T>(Func<Task<T>> func, Nullable<T> defaultValue = null) where T : struct 
{ 
    try 
    { 
     return await func(); 
    } 
    catch (ArgumentException) 
    { 
     return defaultValue; 
    } 
    catch (FormatException) 
    { 
     return defaultValue; 
    } 
    catch (OverflowException) 
    { 
     return defaultValue; 
    } 
} 

예외 처리를 검토해야합니다.이 예제는 일반적인 구문 분석 예외를 처리합니다. InvalidOperationExceptionNotSupportedException과 같은 다른 예외에 반응하는 것이 더 합리적 일 수 있습니다. 프레임 워크 자체에서 가장 많이 사용되는 예외 유형 (반드시 가장 일반적으로 발생되는 예외는 아님)입니다.

또 다른 접근법은 ThreadAbortException과 같은 중요한 예외를 다시 throw하고 기본값을 반환하는 간단한 catch-all 절을 사용하는 것입니다. 그러나 이렇게해도 아무리 심각하더라도 중요하지 않은 모든 예외는 숨겨집니다.

이와 같이 throwing 예외는 값 비싼 연산이기 때문에 일반적으로 TryParse으로 정의되는 Parse입니다. TryGet에 계약이 있어야합니다 (예 : TaskCanceledException을 포함하여 OperationCanceledException을 처리하십시오.

마지막으로 비동기 - 접미사 규칙에 따라 TryGetAsync으로 지정해야합니다. 가능한 결정 [1][2]

1

하나는 0 또는 1 원소를 함유 ParsedFile의 배열이다.

var parsedFiles = await TryDownloadAndParse(url); 
parsedFiles.SingleOrDefault()?.DoVeryImportantWorkWithoutResult(); 

UPDATE

에서 : 당신이 void 메소드를 호출 할 경우

. . . 

var parsedFiles = await TryDownloadAndParse(url); 
if (parsedFiles.Any()) 
{ 
    var parsedFile = parsedFiles.Single(); 
    // more processing 
} 

. . . 

, 당신은 ?. 연산자를 사용할 수 있습니다 :

public async Task<ParsedFile[]> TryDownloadAndParse(string fileUri) 
{ 
    try 
    { 
     return new[] { await DownloadAndParse(fileUri) }; 
    } 
    catch (DownloadNotFoundException ex) 
    { 
     return new ParsedFile[0]; 
    } 
} 

이제 결과를 확인하실 수 있습니다 하늘색 사용하실 수 있습니다 ConditionalValue<TValue> class.

+0

ConditionalValue 재미있는 소리. 그 페이지는 "Reliable Collections API"는 언급하고 있지만 링크는 없으며 자세한 내용은 부족합니다 ... –

+0

PS "신뢰할 수있는 콜렉션"에 대한 더 좋은 링크는 Azure Service Fabric의 서비스 제공 인 것 같습니다. https://docs.microsoft.com/en-us/azure/service-fabric/service-fabric-reliable-services-reliable-collections –