Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved ToString() performance. #8

Merged
merged 1 commit into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 40 additions & 17 deletions src/LightResults/Common/ResultBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Text;

namespace LightResults.Common;
namespace LightResults.Common;

/// <summary>Base class for implementing the <see cref="IResult" /> interface.</summary>
public abstract class ResultBase : IResult
Expand Down Expand Up @@ -54,21 +52,46 @@ public bool HasError<TError>() where TError : IError
/// <inheritdoc />
public override string ToString()
{
var builder = new StringBuilder();
builder.Append(GetType().Name);
builder.Append(" { ");
builder.Append("IsSuccess = ");
builder.Append(IsSuccess);
var typeName = GetType().Name;
if (IsSuccess)
return GetResultString(typeName, "True", "");

if (IsFailed && Errors[0].Message.Length > 0)
{
builder.Append(", Error = ");
builder.Append('"');
builder.Append(Errors[0].Message);
builder.Append('"');
}
var errorString = GetErrorString();
return GetResultString(typeName, "False", errorString);
}

internal static string GetResultString(string typeName, string successString, string informationString)
{
const string preResultStr = " { IsSuccess = ";
const string postResultStr = " }";
#if NET6_0_OR_GREATER
var stringLength = typeName.Length + preResultStr.Length + successString.Length + informationString.Length + postResultStr.Length;

var str = string.Create(stringLength, (typeName, successString, informationString), (span, state) => { span.TryWrite($"{state.typeName}{preResultStr}{state.successString}{state.informationString}{postResultStr}", out _); });

return str;
#else
return $"{typeName}{preResultStr}{successString}{informationString}{postResultStr}";
#endif
}

internal string GetErrorString()
{
if (IsSuccess || Errors[0].Message.Length <= 0)
return "";

var errorMessage = Errors[0].Message;

const string preErrorStr = ", Error = \"";
const string postErrorStr = "\"";
#if NET6_0_OR_GREATER
var stringLength = preErrorStr.Length + errorMessage.Length + postErrorStr.Length;

var str = string.Create(stringLength, errorMessage, (span, state) => { span.TryWrite($"{preErrorStr}{state}{postErrorStr}", out _); });

builder.Append(" }");
return builder.ToString();
return str;
#else
return $"{preErrorStr}{errorMessage}{postErrorStr}";
#endif
}
}
15 changes: 8 additions & 7 deletions src/LightResults/Error.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ namespace LightResults;
public class Error : IError
{
internal static Error Empty { get; } = new();

/// <inheritdoc />
public string Message { get; }

Expand Down Expand Up @@ -41,15 +41,10 @@ public Error(string message)
#endif
}

/// <summary>Initializes a new instance of the <see cref="Error" /> class with the specified metadata.</summary>
/// <param name="metadata">The metadata associated with the error.</param>
public Error((string Key, object Value) metadata) : this("", metadata)
{
}

/// <summary>Initializes a new instance of the <see cref="Error" /> class with the specified metadata.</summary>
/// <param name="metadata">The metadata associated with the error.</param>
public Error(IDictionary<string, object> metadata) : this("", metadata)
public Error((string Key, object Value) metadata) : this("", metadata)
{
}

Expand All @@ -69,6 +64,12 @@ public Error(string message, (string Key, object Value) metadata)
#endif
}

/// <summary>Initializes a new instance of the <see cref="Error" /> class with the specified metadata.</summary>
/// <param name="metadata">The metadata associated with the error.</param>
public Error(IDictionary<string, object> metadata) : this("", metadata)
{
}

/// <summary>Initializes a new instance of the <see cref="Error" /> class with the specified error message and metadata.</summary>
/// <param name="message">The error message.</param>
/// <param name="metadata">The metadata associated with the error.</param>
Expand Down
139 changes: 90 additions & 49 deletions src/LightResults/Result.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System.Text;
using LightResults.Common;

namespace LightResults;
Expand All @@ -10,8 +9,8 @@ public sealed class Result : ResultBase
#endif
{
private static readonly Result OkResult = new();
private static readonly Result FailResult = new(Error.Empty);
private static readonly Result FailedResult = new(Error.Empty);

private Result()
{
}
Expand Down Expand Up @@ -44,15 +43,16 @@ public static Result<TValue> Ok<TValue>(TValue value)
/// <returns>A new instance of <see cref="Result" /> representing a failed result.</returns>
public static Result Fail()
{
return FailResult;
return FailedResult;
}

/// <summary>Creates a failed result with the given error message.</summary>
/// <param name="errorMessage">The error message associated with the failure.</param>
/// <returns>A new instance of <see cref="Result" /> representing a failed result with the specified error message.</returns>
public static Result Fail(string errorMessage)
{
return new Result(new Error(errorMessage));
var error = new Error(errorMessage);
return Fail(error);
}

/// <summary>Creates a failed result with the given error message and metadata.</summary>
Expand All @@ -61,7 +61,8 @@ public static Result Fail(string errorMessage)
/// <returns>A new instance of <see cref="Result" /> representing a failed result with the specified error message and metadata.</returns>
public static Result Fail(string errorMessage, (string Key, object Value) metadata)
{
return new Result(new Error(errorMessage, metadata));
var error = new Error(errorMessage, metadata);
return Fail(error);
}

/// <summary>Creates a failed result with the given error message and metadata.</summary>
Expand All @@ -70,7 +71,8 @@ public static Result Fail(string errorMessage, (string Key, object Value) metada
/// <returns>A new instance of <see cref="Result" /> representing a failed result with the specified error message and metadata.</returns>
public static Result Fail(string errorMessage, IDictionary<string, object> metadata)
{
return new Result(new Error(errorMessage, metadata));
var error = new Error(errorMessage, metadata);
return Fail(error);
}

/// <summary>Creates a failed result with the given error.</summary>
Expand Down Expand Up @@ -143,6 +145,19 @@ public static Result<TValue> Fail<TValue>(IEnumerable<IError> errors)
{
return Result<TValue>.Fail(errors);
}

/// <inheritdoc />
public override string ToString()
{
if (IsSuccess)
return $"{nameof(Result)} {{ IsSuccess = True }}";

if (Errors[0].Message.Length == 0)
return $"{nameof(Result)} {{ IsSuccess = False }}";

var errorString = GetErrorString();
return GetResultString(nameof(Result), "False", errorString);
}
}

/// <summary>Represents a result.</summary>
Expand All @@ -154,7 +169,7 @@ public sealed class Result<TValue> : ResultBase
, IResult<TValue>
#endif
{
private static readonly Result<TValue> FailResult = new(Error.Empty);
private static readonly Result<TValue> FailedResult = new(Error.Empty);

/// <summary>Gets the value of the result, throwing an exception if the result is failed.</summary>
/// <exception cref="InvalidOperationException">Thrown when attempting to get or set the value of a failed result.</exception>
Expand Down Expand Up @@ -210,15 +225,16 @@ public static Result<TValue> Ok(TValue value)
/// <returns>A new instance of <see cref="Result{TValue}" /> representing a failed result.</returns>
public static Result<TValue> Fail()
{
return FailResult;
return FailedResult;
}

/// <summary>Creates a failed result with the given error message.</summary>
/// <param name="errorMessage">The error message associated with the failure.</param>
/// <returns>A new instance of <see cref="Result{TValue}" /> representing a failed result with the specified error message.</returns>
public static Result<TValue> Fail(string errorMessage)
{
return new Result<TValue>(new Error(errorMessage));
var error = new Error(errorMessage);
return Fail(error);
}

/// <summary>Creates a failed result with the given error message and metadata.</summary>
Expand All @@ -227,7 +243,8 @@ public static Result<TValue> Fail(string errorMessage)
/// <returns>A new instance of <see cref="Result{TValue}" /> representing a failed result with the specified error message.</returns>
public static Result<TValue> Fail(string errorMessage, (string Key, object Value) metadata)
{
return new Result<TValue>(new Error(errorMessage, metadata));
var error = new Error(errorMessage, metadata);
return Fail(error);
}

/// <summary>Creates a failed result with the given error message and metadata.</summary>
Expand All @@ -236,7 +253,8 @@ public static Result<TValue> Fail(string errorMessage, (string Key, object Value
/// <returns>A new instance of <see cref="Result{TValue}" /> representing a failed result with the specified error message.</returns>
public static Result<TValue> Fail(string errorMessage, IDictionary<string, object> metadata)
{
return new Result<TValue>(new Error(errorMessage, metadata));
var error = new Error(errorMessage, metadata);
return Fail(error);
}

/// <summary>Creates a failed result with the given error.</summary>
Expand All @@ -258,50 +276,73 @@ public static Result<TValue> Fail(IEnumerable<IError> errors)
/// <inheritdoc />
public override string ToString()
{
var builder = new StringBuilder();
builder.Append(nameof(Result));
builder.Append(" { ");
builder.Append("IsSuccess = ");
builder.Append(IsSuccess);

if (IsSuccess)
{
if (Value is bool || Value is sbyte || Value is byte || Value is short || Value is ushort || Value is int || Value is uint || Value is long || Value is ulong ||
var valueString = GetValueString();
return GetResultString(nameof(Result), "True", valueString);
}

if (Errors[0].Message.Length == 0)
return $"{nameof(Result)} {{ IsSuccess = False }}";

var errorString = GetErrorString();
return GetResultString(nameof(Result), "False", errorString);
}

private string GetValueString()
{
if (IsFailed)
return "";

var valueString = Value?.ToString() ?? "";

const string preValueStr = ", Value = ";
const string charStr = "'";
const string stringStr = "\"";

if (Value is bool || Value is sbyte || Value is byte || Value is short || Value is ushort || Value is int || Value is uint || Value is long || Value is ulong ||
#if NET7_0_OR_GREATER
Value is Int128 || Value is UInt128 ||
Value is Int128 || Value is UInt128 ||
#endif
Value is decimal || Value is float || Value is double)
{
#if NET6_0_OR_GREATER
var stringLength = preValueStr.Length + valueString.Length;

var str = string.Create(stringLength, valueString, (span, state) => { span.TryWrite($"{preValueStr}{state}", out _); });

return str;
#else
return $"{preValueStr}{valueString}";
#endif
Value is decimal || Value is float || Value is double)
{
builder.Append(", Value = ");
builder.Append(Value);
}

if (Value is char)
{
builder.Append(", Value = ");
builder.Append('\'');
builder.Append(Value);
builder.Append('\'');
}

if (Value is string)
{
builder.Append(", Value = ");
builder.Append('"');
builder.Append(Value);
builder.Append('"');
}
}

if (IsFailed && Errors[0].Message.Length > 0)
if (Value is char)
{
builder.Append(", Error = ");
builder.Append('"');
builder.Append(Errors[0].Message);
builder.Append('"');
#if NET6_0_OR_GREATER
var stringLength = preValueStr.Length + charStr.Length + valueString.Length + charStr.Length;

var str = string.Create(stringLength, valueString, (span, state) => { span.TryWrite($"{preValueStr}{charStr}{state}{charStr}", out _); });

return str;
#else
return $"{preValueStr}{charStr}{valueString}{charStr}";
#endif
}

builder.Append(" }");
return builder.ToString();
if (Value is string)
{
#if NET6_0_OR_GREATER
var stringLength = preValueStr.Length + stringStr.Length + valueString.Length + stringStr.Length;

var str = string.Create(stringLength, valueString, (span, state) => { span.TryWrite($"{preValueStr}{stringStr}{state}{stringStr}", out _); });

return str;
#else
return $"{preValueStr}{stringStr}{valueString}{stringStr}";
#endif
}

return "";
}
}
}
Loading
Loading