Pár malých typů, které dělají C# bezpečnějším.
Kalicz.StrongTypes je C# NuGet balíček, který přidává bezkódové validace vynucené překladačem. Neplatná data se nikdy nedostanou do vašeho aplikačního kódu.
Proč se obtěžovat se silnými typy?
Přestaňte validovat vstup všude. Validujte jednou, na hranici, a zbytek nechte vynutit překladač.
Invarianty v typu
Pokud metoda přijímá NonEmptyString, prázdný řetězec se k ní nedostane. Práci odvede překladač — ne strážní podmínka v runtime.
Validace na hranici
Vestavěné konvertory pro System.Text.Json odmítnou neplatná data při deserializaci, ještě než se zavolá handler endpointu.
Výstižný záměr
Signatura jako Result<Order, OrderError> Place(Positive<int> qty) čtenáři přesně říká, co je dovoleno a co může selhat.
Malé a zaměřené
Žádný těžký framework, žádné runtime kouzlení s reflexí. Přidá se vedle existujícího kódu — typy se kombinují s tím, co už máte.
Co je uvnitř
Validované obaly
-
NonEmptyStringZaručeně nenulový, neprázdný řetězec, který není ani jen samé bílé znaky.
-
Positive<T>, NonNegative<T>, Negative<T>, NonPositive<T>Obaly vynucující znaménko nad libovolným INumber<T>.
-
NonEmptyEnumerable<T>Read-only sekvence s alespoň jedním prvkem — Head a Tail jsou vždy bezpečně přístupné.
Algebraické typy
-
Maybe<T>Volitelné hodnoty s Map a FlatMap. Hodí se pro HTTP PATCH (neměnit / vyprázdnit / nastavit).
-
Result<T, TError>Explicitní zacházení s chybami bez výjimek. Včetně podpory async.
Vestavěné integrace
-
System.Text.JsonAutomatické konvertory — bez nutnosti registrace.
-
EF Core value converterySkrze doplňkový balíček Kalicz.StrongTypes.EfCore.
-
Generátory pro FsCheckSkrze doplňkový balíček Kalicz.StrongTypes.FsCheck pro property-based testing.
Jak to vypadá
Pár ukázek pro představu. Plnou dokumentaci a další příklady najdete na GitHubu.
Validace na hranici API
JSON konvertor odmítne neplatný vstup při deserializaci. Váš endpoint nikdy neuvidí špatnou hodnotu.
public record CreateUserRequest(
NonEmptyString Name,
Positive<int> Age);
[HttpPost]
public async Task<IResult> Create(CreateUserRequest req)
{
// Žádná další validace tu není potřeba:
// ASP pipeline odmítne neplatný vstup s 400.
_users.Add(req.Name, req.Age);
await _users.SaveChangesAsync();
return Results.NoContent();
} Explicitní vytváření hodnot
Každý typ má Create (vyhodí výjimku při neplatném vstupu) a TryCreate (vrátí null). Stringové typy mají i plynulou formu rozšíření.
// Stringy — tři varianty volání // throws při neplatném vstupu NonEmptyString name = NonEmptyString.Create(input); // null při neplatném NonEmptyString? safe = NonEmptyString.TryCreate(input); // fluent rozšíření NonEmptyString? fluent = input.AsNonEmpty(); // Čísla — vynucené znaménko nad INumber<T> Positive<int> qty = Positive<int>.Create(5); NonNegative<decimal>? price = amount.AsNonNegative(); // Sekvence — alespoň jeden prvek, Head bezpečné NonEmptyEnumerable<string> tags = ["red", "blue"]; NonEmptyEnumerable<string>? maybe = list.AsNonEmpty(); string first = tags.Head;
Explicitní chyby s Result<T, TError>
Pattern matching na Success nebo Error — žádné výjimky, žádné out parametry.
Result<int, string> Parse(string s) =>
int.TryParse(s, out var n) ? n : "not a number";
var result = Parse(input);
if (result.Success is { } value)
Console.WriteLine($"got {value}");
if (result.Error is { } msg)
Console.WriteLine($"failed: {msg}"); Tři stavy v PATCH s Maybe<T>
Rozlišení "neměnit", "nastavit na null" a "nastavit na hodnotu" — letitý problém v REST API.
public record PatchUser(Maybe<string>? Nickname);
// null → pole neměnit
// Some(null) → vyprázdnit
// Some("abc") → nastavit
if (request.Nickname is { } change)
user.Nickname = change.Value; Skládání s Maybe<T>
Řetězení volitelných operací bez vnořených null kontrol. Implicitní operátory zařídí zabalení — vrátíte T nebo Maybe.None a funguje to.
// Producent: implicitní převod z T nebo Maybe.None
Maybe<int> Parse(string s) =>
int.TryParse(s, out var n) ? n : Maybe.None;
// Map: T -> U, automaticky zabalí v Maybe
Maybe<int> doubled = Parse("21").Map(x => x * 2);
// Some(42)
// FlatMap: T -> Maybe<U>, bez dvojitého zabalení
Maybe<int> sum = Parse("1").FlatMap(a =>
Parse("2").Map(b => a + b));
// Some(3) Začínáme
Zdrojový kód, balíčky a dokumentace.
GitHub repozitář
Zdrojový kód, README, releases a issue tracker.
KaliCZ/StrongTypesNuGet — Core
Hlavní balíček. Obsahuje všechny validované a algebraické typy.
Kalicz.StrongTypesNuGet — EF Core
Value convertery pro ukládání StrongTypes přes Entity Framework Core.
Kalicz.StrongTypes.EfCoreNuGet — FsCheck
Generátory pro property-based testing hodnot StrongTypes.
Kalicz.StrongTypes.FsCheck