Last active
June 12, 2021 00:03
-
-
Save TorstenC/c321407b9ec0c572935f98f6dbf592e6 to your computer and use it in GitHub Desktop.
Recoverable exceptions in C# like in Rust lang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//File Program.cs | |
using System; | |
using static TiMaModelV1.ErrorHandling.LotteryTicketValidator; | |
namespace TiMaModelV1 | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
for (int i = 0; i < 10; i++) | |
{ | |
// Vorteil: | |
// * Nur ein Rückgabewert, und der sieht fast so aus wie in Rust | |
// Nachteile: | |
// * Der Rückgabewert wird auf dem Heap übergeben | |
// * Viel Boilerplate Code | |
var Result = Validate1("123"); | |
if (Result.IsOK) | |
{ | |
Console.WriteLine("Gültig, " + Result.Value.ToString()); | |
} | |
else | |
{ | |
Console.WriteLine("Problem, " + Result.Exception.ToString()); | |
} | |
// Vorteil: | |
// * Der Rückgabewert wird auf dem Stack übergeben | |
// Nachteile: | |
// * Die Funktion muss immer für beide Rückgaben einen Wert angeben | |
// * Zusätzlicher Parameter in der Funktionssignatur | |
var Result2 = Validate2("123", out var Error); | |
if (Result2.HasValue) | |
{ | |
Console.WriteLine("Gültig, " + Result2.Value.ToString()); | |
} | |
else | |
{ | |
Console.WriteLine("Problem, " + Error.ToString()); | |
} | |
} | |
} | |
} | |
} | |
// file Result.cs | |
using System; | |
using System.Security.Cryptography; | |
// https://softwareengineering.stackexchange.com/questions/405038/result-object-vs-throwing-exceptions | |
namespace TiMaModelV1.ErrorHandling | |
{ | |
public interface IError { } | |
public interface IResult<out T, out E> where E : Exception | |
{ | |
T Value { get; } | |
E Exception { get; } | |
bool IsOK { get; } | |
} | |
public struct OK<T, E> : IResult<T, E> where E : Exception | |
{ | |
T IResult<T, E>.Value => Value; | |
E IResult<T, E>.Exception => throw new InvalidOperationException(); | |
bool IResult<T, E>.IsOK => true; | |
public OK(T value) => Value = value; | |
private T Value; | |
} | |
public struct Error<T, E> : IResult<T, E>, IError where E : Exception | |
{ | |
T IResult<T, E>.Value => throw new InvalidOperationException($"No {nameof(IResult<T, E>.Value)} in case of an error."); | |
E IResult<T, E>.Exception => Exception; | |
bool IResult<T, E>.IsOK => false; | |
public Error(E exception) => Exception = exception; | |
private E Exception; | |
} | |
public struct LotteryTicketValidatorError : IResult<LotteryTicketValidity, Exception> | |
{ | |
LotteryTicketValidity IResult<LotteryTicketValidity, Exception>.Value => throw new InvalidOperationException($"No {nameof(IResult<LotteryTicketValidity, Exception>.Value)} in case of an error."); | |
Exception IResult<LotteryTicketValidity, Exception>.Exception => Exception; | |
bool IResult<LotteryTicketValidity, Exception>.IsOK => false; | |
public LotteryTicketValidatorError(Exception exception) => Exception = exception; | |
private Exception Exception; | |
} | |
public static partial class LotteryTicketValidator | |
{ | |
public static IResult<LotteryTicketValidity, Exception> Validate1(string ticketCode) | |
{ | |
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); | |
var random = new byte[1]; | |
rng.GetBytes(random); | |
switch (random[0] % 3) | |
{ | |
case 0: | |
return new OK<LotteryTicketValidity, Exception>(LotteryTicketValidity.YouHaveWon); | |
case 1: | |
return new OK<LotteryTicketValidity, Exception>(LotteryTicketValidity.YouHaveNotWon); | |
default: | |
return new LotteryTicketValidatorError(new Exception("TicketIsInvalid")); | |
//return new Error<LotteryTicketValidity, Exception>(new Exception("TicketIsInvalid")); | |
} | |
} | |
public static LotteryTicketValidity? Validate2(string ticketCode, out Exception exception) | |
{ | |
RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); | |
var random = new byte[1]; | |
rng.GetBytes(random); | |
switch (random[0] % 3) | |
{ | |
case 0: | |
exception = null; | |
return LotteryTicketValidity.YouHaveWon; | |
case 1: | |
exception = null; | |
return LotteryTicketValidity.YouHaveNotWon; | |
default: | |
exception= new Exception("TicketIsInvalid"); | |
return null; | |
} | |
} | |
} | |
public enum LotteryTicketValidity | |
{ | |
YouHaveWon, | |
YouHaveNotWon //, TicketIsInvalid | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I tried error handling like in Rust lang, but I falled back to classic C#, see:
https://softwareengineering.stackexchange.com/questions/405038/result-object-vs-throwing-exceptions