• API Reference

    RecoreFX

    GitHub Actions Badge Azure Pipelines Badge NuGet Badge

    RecoreFX fills the most common needs for C# code after the .NET standard library.

    Installation

    Install from NuGet:

    dotnet add package RecoreFX
    

    Why use it?

    Convenience methods

    If you're like me, there's a bunch of useful methods that you write for every project you work on. Some are simple, such as IDictionary.GetOrAdd(). Others are more subtle, such as SecureCompare.TimeInvariantEquals().

    There's a lot of low-hanging fruit. Want JavaScript-style IIFEs? Write Func.Invoke(). Want ad-hoc RAII like in Go? Create a Defer type. Tired of checking IsAbsoluteUri? Define an AbsoluteUri subtype. (But let's be honest, who really checks?)

    All of this starts to add up, though. That's why I put it all together into a single installable, unit-tested package.

    New stuff

    There are some other goodies here that are farther reaching:

    Optional<T>

    Optional<T> gives you compiler-checked null safety if you don't have nullable references enabled (or if you're on .NET Framework):

    Optional<string> opt = "hello";
    Optional<string> empty = Optional<string>.Empty;
    
    opt.Switch(
        x => Console.WriteLine("Message: " + x),
        () => Console.WriteLine("No message"));
    
    Optional<int> messageLength = opt.OnValue(x => x.Length);
    string message = opt.ValueOr(default);
    

    Either<TLeft, TRight>

    Either<TLeft, TRight> gives you a type-safe union type that will be familiar to TypeScript users:

    Either<string, int> either = "hello";
    
    var message = either.Switch(
        l => $"Value is a string: {l}",
        r => $"Value is an int: {r}");
    

    Result<TValue, TError>

    Result<TValue, TError> gives you a way to handle "expected" errors. You can think of it as a nicer version of the TryParse pattern:

    async Task<Result<Person, HttpStatusCode>> GetPersonAsync(int id)
    {
        var response = await httpClient.GetAsync($"/api/v1/person/{id}");
        if (response.IsSuccessStatusCode)
        {
            var json = await response.Content.ReadAsStringAsync();
            var person = JsonSerializer.Deserialize<Person>(json);
            return Result.Success<Person, HttpStatusCode>(person);
        }
        else
        {
            return Result.Failure<Person, HttpStatusCode>(response.StatusCode);
        }
    }
    

    It also makes it easy to build up an error context as you go along rather than terminating immediately:

    Result<IBlob, IBlob>[] results = await Task.WhenAll(blobsToWrite.Select(blob =>
        Result.TryAsync(async () =>
        {
            await WriteBlobAsync(blob);
            return blob;
        })
        .CatchAsync((Exception e) =>
        {
            Console.Error.WriteLine(e);
            return Task.FromResult(blob);
        })));
    
    List<IBlob> successes = results.Successes().ToList();
    List<IBlob> failures = results.Failures().ToList();
    

    Of<T>

    Of<T> makes it easy to define "type aliases."

    Consider a method with the signature:

    void AddRecord(string address, string firstName, string lastName)
    

    It's easy to make mistakes like this:

    AddRecord("Jane", "Doe", "1 Microsoft Way"); // oops!
    

    You can prevent this with strong typing:

    class Address : Of<string> {}
    
    void AddRecord(Address address, string firstName, string lastName) {}
    
    AddRecord("Jane", "Doe", "1 Microsoft Way"); // compiler error
    

    While defining a new type that behaves the same way string does usually takes a lot of boilerplate, Of<T> handles this automatically:

    var address = new Address { Value = "1 Microsoft Way" };
    Console.WriteLine(address); // prints "1 Microsoft Way"
    
    var address2 = new Address { Value = "1 Microsoft Way" };
    Console.WriteLine(address == address2); // prints "true"
    

    Pipeline<T>

    Pipeline<T> gives you a way to call any method with postfix syntax:

    var result = Pipeline.Of(value)
        .Then(Foo)
        .Then(Bar)
        .Then(Baz)
        .Result;
    

    Defer

    Defer is analogous to Golang's defer statement. It lets you do some kind of cleanup before exiting a method.

    The classic way to do this in C# is with try-finally:

    try
    {
        Console.WriteLine("Doing stuff");
    }
    finally
    {
        Console.WriteLine("Running cleanup");
    }
    

    With Defer and C# 8's new using declarations, we can do it more simply:

    using var cleanup = new Defer(() => Console.WriteLine("Running cleanup"));
    Console.WriteLine("Doing stuff");
    

    Unit

    Unit is a type with only one value (like how Void is a type with no values).

    Imagine a type ApiResult<T> that wraps the deserialized JSON response from a REST API. Without Unit, you'd have to define a separate, non-generic ApiResult type for when the response doesn't have a body:

    ApiResult postResult = await PostPersonAsync(person);
    ApiResult<Person> getResult = await GetPersonAsync(id);
    

    With Unit, you can just reuse the same type:

    ApiResult<Unit> postResult = await PostPersonAsync(person);
    ApiResult<Person> getResult = await GetPersonAsync(id);
    

    In the definition of PostPersonAsync(), just return Unit.Value:

    ApiResult<Unit> PostPersonAsync(Person person)
    {
        // ...
        return new ApiResult<Unit>(Unit.Value);
    }
    

    These are all borrowed from functional programming, but the goal here isn't to turn C# into F#. RecoreFX is meant to encourage more expressive, type-safe code that's still idiomatic C#.

    What's in it?

    Recore

    • AbsoluteUri and RelativeUri
    • AsyncAction, AsyncAction<T>, etc.
    • AsyncFunc<TResult>, AsyncFunc<T, TResult>, etc.
    • Composer<TValue, TResult> and Pipeline<T>
    • Defer
    • Either<TLeft, TRight>, Optional<T>, and Result<TValue, TError>
    • Func
    • Of<T>
    • Unit

    Recore.Collections.Generic

    • AnonymousEqualityComparer<T>
    • MappedComparer<T, TMapped>
    • MappedEqualityComparer<T, TMapped>
    • Extension methods:
      • ICollection<T>.Append()
      • IDictionary<TKey, TValue>.AddRange()
      • IDictionary<TKey, TValue>.GetOrAdd()
      • IDictionary<TKey, TValue>.GetValueOrDefault()
      • LinkedList<T>.Add()

    Recore.Linq

    • IEnumerable<T>.Argmax()
    • IEnumerable<T>.Argmin()
    • IEnumerable<T>.Enumerate()
    • IEnumerable<T>.Flatten()
    • IEnumerable<T>.ForEach()
    • IEnumerable<T>.NonNull()
    • IEnumerable<T>.Product()
    • IEnumerable<T>.ToLinkedList()
    • IEnumerable<T>.Zip()
    • IEnumerable<KeyValuePair<TKey, TValue>>.OnKeys()
    • IEnumerable<KeyValuePair<TKey, TValue>>.OnValues()
    • IEnumerable<KeyValuePair<TKey, TValue>>.ToDictionary()

    Recore.Security.Cryptography

    • Ciphertext<THash>
    • SecureCompare

    Recore.Threading.Tasks

    • Task.Synchronize()
    • Task<T>.Synchronize()

    Contributing

    Have a look at the contributor's guide.

    FAQs

    Why doesn't this have $TYPE or $METHOD?

    If it's generally useful (as opposed to oriented towards a specific application) and fills a common need in C# programming, then there's no reason why not! Feel free to open an issue or submit a PR for discussion.

    How does this compare to $LIBRARY?

    The design principles of RecoreFX are:

    1. Generally useful
    2. Common sense-ness
    3. Follows the programming paradigm of standard C#

    If you like RecoreFX, check out these other libraries:

    • louthy/language-ext
    • mcintyre321/OneOf
    • morelinq/MoreLINQ
    • StephenCleary/AsyncEx

    Does this work with .NET Framework?

    RecoreFX v1 targets .NET Standard 2.0, so it works with .NET Framework ≥ 4.6.1.

    Sample App

    The RecoreFX Sample App is a fully worked out Web app with a console app client using RecoreFX.

    Reference

    https://recorefx.github.io

    • Improve this Doc
    Back to top Generated by DocFX