Skip to content

recorefx/RecoreFX

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

181 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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

Recore.Collections.Generic

Recore.Linq

Recore.Security.Cryptography

Recore.Threading.Tasks

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:

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

About

A simple library for a better C#

Resources

License

Contributing

Stars

Watchers

Forks

Contributors