Skip to content

Instantly share code, notes, and snippets.

@TorstenC
Last active June 12, 2021 00:03
Show Gist options
  • Save TorstenC/c321407b9ec0c572935f98f6dbf592e6 to your computer and use it in GitHub Desktop.
Save TorstenC/c321407b9ec0c572935f98f6dbf592e6 to your computer and use it in GitHub Desktop.
Recoverable exceptions in C# like in Rust lang
//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
}
}
@TorstenC
Copy link
Author

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment