Причина попередження пояснюється в розділі The issue with T?
" Випробуйте Nullable посилальних типів . Короткий опис короткого оповідання, якщо ви користуєтеся, T?
вам слід вказати, чи є тип класом чи структурою Ви можете створити два типи для кожного випадку.
Більш глибока проблема полягає в тому, що використання одного типу для реалізації Result і утримування значень Success і Error повертає ті самі проблеми, які Result повинен був виправити, та ще кілька.
- Один і той же тип повинен нести мертве значення навколо типу або помилки або повертати нулі
- Узгодження шаблону за типом неможливо. Вам потрібно буде використовувати деякі вигадливі вирази відповідності позиційного шаблону, щоб дозволити цьому працювати.
- Щоб уникнути нулів, вам доведеться використовувати щось на зразок Опція / Можливо, схоже на Параметри F # . Ви все одно несете "Нічого" навколо значення або помилки.
Результат (і в будь-якому випадку) у F #
Початковою точкою повинен бути тип результату F # та дискриміновані спілки. Зрештою, це вже працює на .NET.
Тип результату в F # є:
type Result<'T,'TError> =
| Ok of ResultValue:'T
| Error of ErrorValue:'TError
Самі типи несуть лише те, що їм потрібно.
DU в F # дозволяють вичерпне узгодження шаблону, не вимагаючи нулів:
match res2 with
| Ok req -> printfn "My request was valid! Name: %s Email %s" req.Name req.Email
| Error e -> printfn "Error: %s" e
Емуляція цього в C # 8
На жаль, у C # 8 ще немає DU, вони заплановані на C # 9. У C # 8 ми можемо наслідувати це, але ми втрачаємо вичерпну відповідність:
#nullable enable
public interface IResult<TResult,TError>{}
struct Success<TResult,TError> : IResult<TResult,TError>
{
public TResult Value {get;}
public Success(TResult value)=>Value=value;
public void Deconstruct(out TResult value)=>value=Value;
}
struct Error<TResult,TError> : IResult<TResult,TError>
{
public TError ErrorValue {get;}
public Error(TError error)=>ErrorValue=error;
public void Deconstruct(out TError error)=>error=ErrorValue;
}
І використовуйте:
IResult<double,string> Sqrt(IResult<double,string> input)
{
return input switch {
Error<double,string> e => e,
Success<double,string> (var v) when v<0 => new Error<double,string>("Negative"),
Success<double,string> (var v) => new Success<double,string>(Math.Sqrt(v)),
_ => throw new ArgumentException()
};
}
Без вичерпного узгодження шаблону ми мусимо додати це застереження за замовчуванням, щоб уникнути попереджень компілятора.
Я все ще шукаю спосіб отримати вичерпну відповідність без введення мертвих значень, навіть якщо вони є лише варіантом.
Варіант / Можливо
Створення класу Option способом, що використовує вичерпне узгодження, простіше:
readonly struct Option<T>
{
public readonly T Value {get;}
public readonly bool IsSome {get;}
public readonly bool IsNone =>!IsSome;
public Option(T value)=>(Value,IsSome)=(value,true);
public void Deconstruct(out T value,out bool isSome)=>(value,isSome)=(Value,IsSome);
}
//Convenience methods, similar to F#'s Option module
static class Option
{
public static Option<T> Some<T>(T value)=>new Option<T>(value);
public static Option<T> None<T>()=>default;
}
Які можна використовувати з:
string cateGory = someValue switch { Option<Category> (_ ,false) =>"No Category",
Option<Category> (var v,true) => v.Name
};