[英]Avoiding Arrow Pattern with C# Pattern Matching
我已經開始使用 C# 7 的基於類型的模式匹配。 只管理單個基於模式的結果的方法看起來非常干凈且易於推理。
但是,一旦依賴於第一個基於模式的結果的第二個基於模式的結果創建了一個箭頭反模式,並且只會在 n 個結果相互依賴時變得更糟。
這是一個演示箭頭模式的簡化示例:
public Result<bool> ValidateSomething(string strId)
{
var id = Guid.Parse(strId);
Result<Something> fetchSomethingResult = new SomethingDao.FetchSomething(id);
switch (fetchSomethingResult)
{
case ValueResult<Something> successfulSomethingResult:
Result<Related> fetchRelatedFieldsResult = new RelatedDao.FetchRelatedFields(successfulSomethingResult.Value.RelatedId);
switch (fetchRelatedFieldsResult)
{
case ValueResult<Related> successfulFieldValueResult:
var isValid = successfulSomethingResult.Value.ComparableVal <= successfulFieldValueResult.Value.RelatedComparableVal;
return new ValueResult<bool>(isValid);
case ValueNotFoundResult<Related> _:
return new ValueNotFoundResult<bool>();
case ErrorResult<Related> errorResult:
return new ErrorResult<bool>(errorResult.ResultException);
default:
throw new NotImplementedException("Unexpected Result Type Received.");
}
case ValueNotFoundResult<Something> notFoundResult:
return new ValueNotFoundResult<bool>();
case ErrorResult<Something> errorResult:
return new ErrorResult<bool>(errorResult.ResultException);
default:
throw new NotImplementedException("Unexpected Result Type Received.");
}
}
作為參考,這些是Result
類的定義:
public abstract class Result<T>
{
}
public class ValueResult<T> : Result<T>
{
public ValueResult()
{
}
public ValueResult(T inValue)
{
Value = inValue;
}
public T Value { get; set; }
}
public class ValueNotFoundResult<T> : Result<T>
{
public ValueNotFoundResult()
{
}
}
public class ErrorResult<T> : Result<T>
{
public Exception ResultException { get; set; }
public ErrorResult()
{
}
public ErrorResult(Exception resultException)
{
ResultException = resultException;
}
}
有哪些選項可以更好地處理此類代碼? 你對前面的例子有什么建議? 如何使用基於模式的結果避免箭頭反模式?
由於您只是在結果成功的情況下才真正對結果進行任何操作,因此將開關移動到擴展方法似乎相當簡單。 您可能需要兩個擴展,具體取決於 function 是返回新結果還是僅返回 T,例如:
public static Result<TOut> SelectValue<TIn, TOut>(this Result<TIn> self, Func<TIn, Result<TOut>> select)
{
switch (self)
{
case ErrorResult<TIn> errorResult: return new ErrorResult<TOut>(errorResult.ResultException);
case ValueNotFoundResult<TIn> valueNotFoundResult: return new ValueNotFoundResult<TOut>();
case ValueResult<TIn> valueResult: return select(valueResult.Value);
default: throw new ArgumentOutOfRangeException(nameof(self));
}
}
public static Result<TOut> SelectValue<TIn, TOut>(this Result<TIn> self, Func<TIn, TOut> select)
{
switch (self)
{
case ErrorResult<TIn> errorResult: return new ErrorResult<TOut>(errorResult.ResultException);
case ValueNotFoundResult<TIn> valueNotFoundResult: return new ValueNotFoundResult<TOut>();
case ValueResult<TIn> valueResult: return new ValueResult<TOut>(select(valueResult.Value));
default: throw new ArgumentOutOfRangeException(nameof(self));
}
}
然后,您可以像這樣處理成功案例:
var result = fetchSomethingResult
.SelectValue(something => new RelatedDao.FetchRelatedFields(something.RelatedId))
.SelectValue(related => related.CompareVal >= 5);
如果您需要同時使用“Something”和“Related”中的值,很遺憾您需要嵌套調用。
var result = fetchSomethingResult
.SelectValue(something => new RelatedDao.FetchRelatedFields(something.RelatedId).SelectValue(related => related.CompareVal >= 5));
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.