Alasan untuk peringatan dijelaskan di bagian The issue with T?
dari Try out Jenis Nullable Referensi . Singkat cerita, jika Anda menggunakan T?
Anda harus menentukan apakah jenisnya adalah kelas atau struct. Anda dapat membuat dua jenis untuk setiap kasus.
Masalah yang lebih dalam adalah bahwa menggunakan satu jenis untuk mengimplementasikan Hasil dan menahan nilai Sukses dan Kesalahan mengembalikan masalah yang sama Hasil seharusnya diperbaiki, dan beberapa lagi.
- Tipe yang sama harus membawa nilai mati, baik tipe atau kesalahan, atau mengembalikan nol
- Pencocokan pola pada tipe tidak dimungkinkan. Anda harus menggunakan beberapa ekspresi pencocokan pola posisi mewah untuk membuatnya bekerja.
- Untuk menghindari null, Anda harus menggunakan sesuatu seperti Option / Maybe, mirip dengan Opsi F # . Anda masih membawa Tidak ada di sekitar, baik untuk nilai atau kesalahan.
Hasil (dan Baik) di F #
Titik awal haruslah tipe Hasil F # dan serikat yang didiskriminasi. Bagaimanapun, ini sudah berfungsi di .NET.
Jenis hasil dalam F # adalah:
type Result<'T,'TError> =
| Ok of ResultValue:'T
| Error of ErrorValue:'TError
Jenis-jenis itu sendiri hanya membawa apa yang mereka butuhkan.
DU dalam F # memungkinkan pencocokan pola yang lengkap tanpa membutuhkan nol:
match res2 with
| Ok req -> printfn "My request was valid! Name: %s Email %s" req.Name req.Email
| Error e -> printfn "Error: %s" e
Meniru ini dalam C # 8
Sayangnya, C # 8 belum memiliki DU, mereka dijadwalkan untuk C # 9. Di C # 8 kita dapat meniru ini, tetapi kami kehilangan pencocokan lengkap:
#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;
}
Dan gunakan itu:
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()
};
}
Tanpa pencocokan pola yang lengkap, kita harus menambahkan klausa default untuk menghindari peringatan kompiler.
Saya masih mencari cara untuk mendapatkan kecocokan lengkap tanpa memperkenalkan nilai mati, bahkan jika mereka hanya sebuah Opsi.
Opsi / Mungkin
Membuat kelas Opsi dengan cara yang menggunakan pencocokan lengkap lebih mudah:
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;
}
Yang dapat digunakan dengan:
string cateGory = someValue switch { Option<Category> (_ ,false) =>"No Category",
Option<Category> (var v,true) => v.Name
};