Lý do cho sự cảnh báo được giải thích trong phần The issue with T?
của thử loại Nullable tham khảo . Câu chuyện dài, nếu bạn sử dụng, T?
bạn phải xác định loại là lớp hay cấu trúc. Bạn có thể sẽ tạo ra hai loại cho mỗi trường hợp.
Vấn đề sâu xa hơn là việc sử dụng một loại để triển khai Kết quả và giữ cả hai giá trị Thành công và Lỗi mang lại cùng một vấn đề Kết quả được cho là phải khắc phục và một vài vấn đề khác.
- Loại tương tự phải mang một giá trị chết xung quanh, loại hoặc lỗi hoặc mang lại null
- Khớp mẫu trên loại không thể. Bạn sẽ phải sử dụng một số biểu thức khớp mẫu vị trí ưa thích để làm việc này.
- Để tránh null, bạn sẽ phải sử dụng một cái gì đó như Tùy chọn / Có thể, tương tự như Tùy chọn của F # . Mặc dù vậy, bạn vẫn sẽ mang theo Không có giá trị hoặc lỗi.
Kết quả (và Either) trong F #
Điểm bắt đầu phải là loại Kết quả của F # và các hiệp hội phân biệt đối xử. Rốt cuộc, điều này đã hoạt động trên .NET.
Loại kết quả trong F # là:
type Result<'T,'TError> =
| Ok of ResultValue:'T
| Error of ErrorValue:'TError
Các loại bản thân chỉ mang những gì họ cần.
Các DU trong F # cho phép khớp mẫu đầy đủ mà không yêu cầu null:
match res2 with
| Ok req -> printfn "My request was valid! Name: %s Email %s" req.Name req.Email
| Error e -> printfn "Error: %s" e
Thi đua này trong C # 8
Thật không may, C # 8 chưa có DU, chúng được lên lịch cho C # 9. Trong C # 8, chúng tôi có thể mô phỏng điều này, nhưng chúng tôi mất kết hợp hoàn hảo:
#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;
}
Và sử dụng nó:
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()
};
}
Nếu không khớp mẫu đầy đủ, chúng ta phải thêm mệnh đề mặc định đó để tránh các cảnh báo của trình biên dịch.
Tôi vẫn đang tìm cách để có được kết hợp hoàn hảo mà không đưa ra các giá trị chết, ngay cả khi chúng chỉ là một Tùy chọn.
Tùy chọn / Có thể
Tạo một lớp Tùy chọn bằng cách sử dụng kết hợp toàn diện đơn giản hơn:
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;
}
Mà có thể được sử dụng với:
string cateGory = someValue switch { Option<Category> (_ ,false) =>"No Category",
Option<Category> (var v,true) => v.Name
};