Skip to content

Commit

Permalink
Improved ToString() performance. (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
jscarle authored Feb 20, 2024
1 parent 090524f commit 67a7f58
Show file tree
Hide file tree
Showing 5 changed files with 380 additions and 110 deletions.
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

0 comments on commit 67a7f58

Please sign in to comment.