ForgeSharp.Results
1.1.0
dotnet add package ForgeSharp.Results --version 1.1.0
NuGet\Install-Package ForgeSharp.Results -Version 1.1.0
<PackageReference Include="ForgeSharp.Results" Version="1.1.0" />
<PackageVersion Include="ForgeSharp.Results" Version="1.1.0" />
<PackageReference Include="ForgeSharp.Results" />
paket add ForgeSharp.Results --version 1.1.0
#r "nuget: ForgeSharp.Results, 1.1.0"
#:package ForgeSharp.Results@1.1.0
#addin nuget:?package=ForgeSharp.Results&version=1.1.0
#tool nuget:?package=ForgeSharp.Results&version=1.1.0
ForgeSharp.Results
ForgeSharp.Results is a high-performance, type-safe, and allocation-free result monad for .NET. It provides a robust alternative to exceptions for error handling, enabling explicit, predictable, and maintainable code.
Features
Performance by Design:
All result types are implemented as readonly structs, ensuring zero heap allocations and minimal GC pressure. This makes ForgeSharp.Results ideal for high-throughput and low-latency applications.Explicit Error Handling:
The result pattern replaces exceptions for flow control, making error and success states explicit and easy to reason about.Type Safety & Clarity:
APIs are strongly-typed and expressive, enabling compile-time safety and clear intent in your code.Modern C# Best Practices:
Leverages the latest C# features and .NET standards for maximum compatibility and future-proofing.Composable Pipelines:
Build synchronous and asynchronous pipelines for result-based workflows, with full LINQ support for expressive, query-based composition.
Pipelines & LINQ Integration
ForgeSharp.Results introduces Pipelines for composing complex workflows in a functional, type-safe, and allocation-free manner. Pipelines can be synchronous (IPipeline<T>
) or asynchronous (IAsyncPipeline<T>
), and can be created from delegates or composed from other pipelines.
- LINQ Support: Pipelines can be composed using LINQ query expressions, enabling powerful, readable, and maintainable workflows. LINQ methods like
select
,where
, andfrom
are supported for both sync and async pipelines. - Combining Results: Pipelines are ideal for combining multiple result-producing operations, propagating errors automatically, and building robust data flows.
_Example:
var pipeline =
from user in GetUserPipeline()
where user.IsActive
from profile in GetProfilePipeline(user.Id)
select (user, profile);
var result = pipeline.Execute();
if (result.IsSuccess)
Console.WriteLine($"User: {result.Value.user}, Profile: {result.Value.profile}");
Extension Methods
ForgeSharp.Results provides a rich set of extension methods to make working with results functional, expressive, and safe in both synchronous and asynchronous scenarios.
Chaining and Functional Composition
- Then / ThenAsync
Chain operations that return results, propagating failures automatically.
Example:
result.Then(x => DoSomething(x))
await result.ThenAsync(x => DoSomethingAsync(x))
Value Extraction and Conversion
GetOrDefault / GetOrDefaultAsync
Get the value if successful, or a default value if not.
Example:
result.GetOrDefault(-1)
await resultTask.GetOrDefaultAsync(-1)
GetOrThrow / GetOrThrowAsync
Get the value if successful, or throw an exception if not.
Example:
result.GetOrThrow()
await resultTask.GetOrThrowAsync()
AsResult / AsResultAsync
Convert aResult<T>
to a non-genericResult
.
Example:
result.AsResult()
await resultTask.AsResultAsync()
Null Safety
- EnsureNotNull / EnsureNotNullAsync
Ensure the value of a successful result is not null, otherwise return a failed result.
Example:
result.EnsureNotNull()
await resultTask.EnsureNotNullAsync()
Tapping (Side Effects)
- Tap / TapAsync
Execute an action if the result is successful, without altering the result.
Example:
result.Tap(() => Log("Success"))
await result.TapAsync(async () => await LogAsync())
Flattening
- Flatten / FlattenAsync
Flatten nested results or a sequence of results into a single result, propagating the first failure.
Example:
result.Flatten()
await resultTask.FlattenAsync()
Mapping
- Map / MapAsync
Map the values of a successful result sequence to a new type.
Example:
result.Map(x => x.ToString())
await resultTask.MapAsync(x => x.ToString())
Resolving Collections
- ResolveAsync
Asynchronously resolve a collection of result tasks into a collection or async enumerable of results.
Example:
await tasks.ResolveAsync()
Dictionary and Collection Helpers
TryGetValueResult
Attempts to get a value from a dictionary as aResult<T>
.
Example:
var valueResult = dict.TryGetValueResult(key);
FirstOrResult
Returns the first element in a sequence that matches a predicate as aResult<T>
.
Example:
var firstResult = list.FirstOrResult(x => x.IsActive, "No active item found.");
Miscellaneous
Restore / RestoreAsync
Restore a failed result using a provided function, useful for fallback or recovery logic.
Example:
result.Restore(fallbackFunc)
await resultTask.RestoreAsync(fallbackFunc)
IsSuccessAsync
Determine whether an awaited result is successful.
Example:
await resultTask.IsSuccessAsync()
Use Cases
- Error Handling Without Exceptions:
Replace exceptions for flow control with explicit, type-safe results.
Result result = DoWork();
if (!result.IsSuccess)
Console.WriteLine(result.Message);
- Functional Pipelines:
Chain operations and propagate errors fluently.
var result = GetUser()
.Then(user => Validate(user))
.Then(validUser => Save(validUser));
- Async Pipelines:
Use async/await with result types for modern, non-blocking code.
var result = await GetUserAsync()
.ThenAsync(user => ValidateAsync(user));
- LINQ Pipelines:
Use LINQ query expressions to compose pipelines and combine results.
var pipeline =
from user in GetUserPipeline()
where user.IsActive
from profile in GetProfilePipeline(user.Id)
select (user, profile);
var result = pipeline.Execute();
Quick Start
Basic Usage
Results can be used to represent success or failure in operations, with optional messages and exceptions.
Result DoSomething()
{
// Perform some operation
return Result.Success();
}
Result result = DoSomething();
if (!result.IsSuccess)
Console.WriteLine(result.Message);
Results can also carry values, allowing you to return data alongside success or failure states.
Result<int> GetValue()
{
// Perform some operation that returns an int
return Result.Success(42);
}
Result<int> valueResult = GetValue();
int value = valueResult.IsSuccess ? valueResult.Value : -1;
Capturing Exceptions
Result result = Result.Capture(() => MightThrow());
Result<int> valueResult = Result.Capture(() => MightThrowAndReturnInt());
Result result = await Result.CaptureAsync(async () => await MightThrowAsync());
Result<int> valueResult = await Result.CaptureAsync(async () => await MightThrowAndReturnIntAsync());
API Overview
- Result / Result<T>:
Core result types representing success, validation faults, and exceptions. - IResult / IResult<T>:
Interfaces for result types. - Pipelines:
Synchronous and asynchronous pipelines for composing result-based workflows, with LINQ support. - Functional Extension Methods:
For mapping, chaining, error handling, and async workflows. - Dictionary and Collection Helpers:
Methods likeTryGetValueResult
andFirstOrResult
for safer collection access. - Forwarding and Conversion:
Easily convert between generic and non-generic results.
Project Structure
Result.cs
- Core result types and logic.Pipeline.cs
- Pipeline interfaces and implementations.Monad/
- Extension methods for chaining, mapping, flattening, and more.
Requirements
- .NET Standard 2.0, .NET Standard 2.1, or .NET 8.0+
Contributing
Contributions are welcome! Please open issues or submit pull requests for bug fixes, improvements, or new features.
License
Acknowledgements
- Inspired by functional programming and modern .NET design.
- Built for developers who care about performance, safety, and maintainability.
ForgeSharp.Results - Fast, type-safe, and allocation-free result handling for .NET.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
.NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
.NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
.NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
MonoAndroid | monoandroid was computed. |
MonoMac | monomac was computed. |
MonoTouch | monotouch was computed. |
Tizen | tizen40 was computed. tizen60 was computed. |
Xamarin.iOS | xamarinios was computed. |
Xamarin.Mac | xamarinmac was computed. |
Xamarin.TVOS | xamarintvos was computed. |
Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- No dependencies.
-
.NETStandard 2.1
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.