Saya mencoba membuat jenis yang mirip dengan Rust Result
atau Haskell Either
dan saya sudah sejauh ini:
public struct Result<TResult, TError>
where TResult : notnull
where TError : notnull
{
private readonly OneOf<TResult, TError> Value;
public Result(TResult result) => Value = result;
public Result(TError error) => Value = error;
public static implicit operator Result<TResult, TError>(TResult result)
=> new Result<TResult, TError>(result);
public static implicit operator Result<TResult, TError>(TError error)
=> new Result<TResult, TError>(error);
public void Deconstruct(out TResult? result, out TError? error)
{
result = (Value.IsT0) ? Value.AsT0 : (TResult?)null;
error = (Value.IsT1) ? Value.AsT1 : (TError?)null;
}
}
Mengingat bahwa kedua tipe parameter dibatasi notnull
, mengapa mengeluh (di mana saja ada parameter tipe dengan ?
tanda nullable setelahnya) bahwa:
Parameter tipe nullable harus diketahui sebagai tipe nilai atau tipe referensi yang tidak dapat dibatalkan. Pertimbangkan menambahkan 'kelas', 'struct', atau ketik batasan.
?
Saya menggunakan C # 8 di .NET Core 3 dengan jenis referensi nullable diaktifkan.
c#
generics
type-constraints
c#-8.0
nullable-reference-types
Sepatu Diamente
sumber
sumber
Jawaban:
Pada dasarnya Anda meminta sesuatu yang tidak dapat direpresentasikan dalam IL. Tipe nilai nullable dan tipe referensi nullable adalah beast yang sangat berbeda, dan walaupun mereka terlihat serupa dalam kode sumber, IL sangat berbeda. Versi nullable dari tipe nilai
T
adalah tipe yang berbeda (Nullable<T>
) sedangkan versi nullable dari tipe referensiT
adalah tipe yang sama , dengan atribut yang memberitahu kompiler apa yang diharapkan.Pertimbangkan contoh sederhana ini:
Itu tidak valid karena alasan yang sama.
Jika kita membatasi
T
menjadi struct, maka IL yang dihasilkan untukGetNullValue
metode akan memiliki tipe returnNullable<T>
.Jika kita membatasi
T
untuk menjadi tipe referensi yang tidak dapat dibatalkan, maka IL yang dihasilkan untukGetNullValue
metode tersebut akan memiliki tipe pengembalianT
, tetapi dengan atribut untuk aspek nullability.Kompiler tidak dapat menghasilkan IL untuk metode yang memiliki tipe pengembalian keduanya
T
danNullable<T>
pada saat yang sama.Ini pada dasarnya semua hasil dari jenis referensi nullable sama sekali bukan konsep CLR - itu hanya kompiler ajaib untuk membantu Anda mengekspresikan niat dalam kode dan membuat kompiler melakukan beberapa pemeriksaan pada waktu kompilasi.
Pesan kesalahannya tidak sejelas mungkin.
T
dikenal sebagai "tipe nilai atau tipe referensi yang tidak dapat dibatalkan". Pesan kesalahan yang lebih tepat (tapi jauh lebih nyata) adalah:Pada saat itu kesalahan akan berlaku pada kode kami - parameter tipe tidak "dikenal sebagai tipe nilai" dan itu tidak "diketahui sebagai tipe referensi yang tidak dapat dibatalkan". Ini dikenal sebagai salah satu dari dua, tetapi kompiler perlu tahu yang mana .
sumber
Nullable<T>
adalah tipe khusus yang tidak dapat Anda buat sendiri. Dan kemudian ada poin bonus tentang bagaimana tinju dilakukan dengan tipe nulllable.Alasan untuk peringatan dijelaskan di bagian
The issue with T?
dari Try out Jenis Nullable Referensi . Singkat cerita, jika Anda menggunakanT?
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.
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:
Jenis-jenis itu sendiri hanya membawa apa yang mereka butuhkan.
DU dalam F # memungkinkan pencocokan pola yang lengkap tanpa membutuhkan nol:
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:
Dan gunakan itu:
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:
Yang dapat digunakan dengan:
sumber