Struct Error
- Namespace
- Badeend
- Assembly
- Badeend.Error.dll
A lightweight immutable error representation that contains:
- A human-readable error description. (Message)
- (Optional) A custom data payload, used to provide additional context beyond the message. (Data)
- (Optional) An inner error, used to build up a "chain" of context. (InnerError)
public readonly struct Error : IEquatable<Error>, IComparable<Error>, IComparable
- Implements
- Inherited Members
Remarks
This Error type is designed to be a close relative to the built-in Exception class, but with a focus on being lightweight and suitable for situations where errors need to be reported frequently and/or where performance is critical.
The four primary methods for creating new errors are:
- From a message string (Error(string?))
- From an existing Exception instance (Error(Exception?))
- From an
enum
value (FromEnum<TEnum>(TEnum)) - From a custom IError implementation (Error(IError?))
All of these are O(1)
and allocation free.
This type is designed to be used with Badeend.Result
, though it can
be used standalone as well. Note that you should generally not attempt to
derive any semantic meaning from the Error's content.
I.e., Result<T, Badeend.Error>
(or its shorthand Result<T>
)
is semantically the same as Result<T, void>
in that: all that the
domain logic should care about is whether the operation succeeded or failed.
The Error data is just a way to carry additional developer-oriented context.
This type has the size of just a single machine word (4 or 8 bytes), making it a good fit for applications where errors are treated as first-class values, are copied frequently and are propagated through regular control flow patterns instead of stack unwinding.
This type does not collect stack traces by design. Any additional
context that you want to attach along the way must be added manually by
wrapping it inside a new error using one of constructors that take an
InnerError
parameter, e.g. Error(string?, object?, Error?)
.
The default
Error contains only a predefined default message and is
equivalent to using the parameterless constructor.
Constructors
Error()
Create a new empty Error.
[Pure]
public Error()
Remarks
Errors created by this constructor can be used as a "marker" to signal that an operation did not succeed even in the absence of a descriptive message.
This is an O(1)
operation, does not allocate any memory and is
equivalent to the default
value.
Error(IError?)
Create a new Error using the provided error
as the backing implementation.
[Pure]
public Error(IError? error)
Parameters
error
IError
Remarks
This is an O(1)
operation and does not allocate any memory.
Error(Exception?)
Create a new Error from the provided exception
.
[Pure]
public Error(Exception? exception)
Parameters
exception
Exception
Remarks
If the provided exception
was obtained through
AsException(), the original Error is returned to prevent
double wrapping.
Otherwise, this is functionally equivalent to creating an Error with:
- the Message set to the exception's Message,
- the Data set to the exception itself
- the InnerError set to the exception's InnerException that has been recursively converted using the preceding logic.
Error(string?)
Create a new Error with the provided message
.
[Pure]
public Error(string? message)
Parameters
message
string
Remarks
This is an O(1)
operation and does not allocate any memory.
Error(string?, Error?)
Create a new Error with the provided message
that wraps the innerError
.
[Pure]
public Error(string? message, Error? innerError)
Parameters
Error(string?, object?, Error?)
Create a new Error with the provided message
and/or data
that wraps the innerError
.
[Pure]
public Error(string? message, object? data = null, Error? innerError = null)
Parameters
Properties
Data
The payload attached at this specific point in the error chain.
[Pure]
public object? Data { get; }
Property Value
Remarks
Generally, you should not depend on specific payloads existing at specific levels in the error chain as they may change over time due to e.g. internal refactorings or other updates to the code that produces the errors.
If you need to retrieve a specific type of payload, consider using the TryFindData<T>(out T) method instead.
InnerError
Gets the inner error associated with the current Error
instance, or null
if this is a "root" error.
[Pure]
public Error? InnerError { get; }
Property Value
Remarks
This property is similar to the InnerException
property of
regular .NET exceptions. When a new Error is created by
using e.g. Error(string?, object?, Error?), the pre-existing
Error instance becomes the InnerError
of the new
instance. This chaining allows for capturing contextual information at
each layer where the error is encountered, while retaining the original
error details.
Accessing InnerError provides a way to traverse the chain of errors in reverse order, from the most recently appended error back to the original root error.
Message
Human-readable description of the error. A generic fallback string will be returned if no message was provided.
[Pure]
public string Message { get; }
Property Value
Remarks
Error messages are intended for human consumption only and should not be parsed or relied on programmatically. Parsing this message may lead to breaking changes if the message format changes in the future.
Methods
AsException()
Convert the error into an exception. This may return a previously Error-wrapped-Exception as-is. The returned exception should therefore be considered as "already thrown" and should not be thrown directly again. The only supported method for throwing the returned value is as the inner exception of a fresh exception instance.
[Pure]
public Exception AsException()
Returns
Remarks
The returned Exception hierarchy is for debugging purposes only. The format is not stable and may change without prior notice.
CompareTo(Error)
Compare two errors.
Errors are compared by their components: (Message, Data, InnerError)
.
[Pure]
public int CompareTo(Error other)
Parameters
other
Error
Returns
- int
See IComparable<T>.CompareTo(T) for more information.
Exceptions
- ArgumentException
The Data object does not implement IComparable.
Equals(Error)
Check two Error instances for equality. Errors use
structural equality and are considered "equal" when their
(Message, Data, InnerError)
components are equal. The Data
component is compared using the Equals(object) method.
[Pure]
public bool Equals(Error other)
Parameters
other
Error
Returns
FromEnum<TEnum>(TEnum)
Create a new Error from the provided enum value
.
public static Error FromEnum<TEnum>(TEnum value) where TEnum : struct, Enum
Parameters
value
TEnum
Returns
Type Parameters
TEnum
Remarks
The error message can be customized by annotating the enum members with the [ErrorMessage] attribute.
If the value is a regular declared enum member (i.e. it is returned by Enum.GetValues
),
then this is an O(1)
operation and does not allocate any memory.
GetHashCode()
[Pure]
public override int GetHashCode()
Returns
ToString()
Get a string representation of the error for debugging purposes. The format is not stable and may change without prior notice.
[Pure]
public override string ToString()
Returns
TryFindData<T>(out T)
Search through the error chain and attempt to retrieve the payload that
matches the specified type T
.
public bool TryFindData<T>(out T data)
Parameters
data
T
Returns
Type Parameters
T
The type of the payload object to retrieve from the Error.
Remarks
This method provides a type-safe way to access metadata without overly
relying on the order in which the errors are nested. If multiple items
of type T
exist, the top-most instance in the
chain is returned.
Operators
operator ==(Error, Error)
Check two Error instances for equality. Errors use
structural equality and are considered "equal" when their
(Message, Data, InnerError)
components are equal. The Data
component is compared using the Equals(object) method.
[Pure]
public static bool operator ==(Error left, Error right)
Parameters
Returns
operator !=(Error, Error)
Check for inequality. See Equals(Error) for more info.
[Pure]
public static bool operator !=(Error left, Error right)