Files
radzen-blazor/Radzen.Blazor/Documents/Spreadsheet/ColumnRef.cs
Atanas Korchev b05f50782e Route short-lived StringBuilders through StringBuilderCache
Six call sites that build a string once and either return or assign it
now go through the thread-local cache instead of allocating a fresh
StringBuilder on every call:

- NumberFormat.FormatDate
- NumberFormat.FormatNumber (outer builder; the inner thousand-grouping
  builder stays a direct allocation because it is nested inside the
  outer cached one)
- NumberFormat.FormatScientific
- CsvWriter.BuildContent
- Worksheet.InvalidateFormulasReferencing (formula rebuild)
- ColumnRef.ToString — additionally drops the stackalloc<char>;
  appends digits least-significant first then reverses in place via
  the StringBuilder indexer

Sites left as direct 'new StringBuilder()': CsvReader.ParseCsv,
NumberFormatParser.SplitSections / ParseSection, FormulaLexer
ScanTrivia / ScanStringLiteral / ScanNumericLiteral. Each either holds
two parallel builders, reuses one across many fields with Clear, or
has multiple ToString() / return paths that would need try/finally
release plumbing.
2026-06-15 18:45:17 +03:00

82 lines
2.7 KiB
C#

using System;
using System.Text;
namespace Radzen.Documents.Spreadsheet;
#nullable enable
/// <summary>
/// Represents a reference to a column in a spreadsheet.
/// </summary>
public readonly struct ColumnRef(int column) : IEquatable<ColumnRef>
{
/// <summary>
/// Returns the column index for the column reference.
/// The column index is zero-based, so the first column (A) corresponds to 0.
/// </summary>
public int Column { get; } = column;
/// <summary>
/// Checks if the column reference is equal to another column reference.
/// </summary>
public bool Equals(ColumnRef other) => Column == other.Column;
/// <summary>
/// Returns a string representation of the column reference in A1 notation.
/// For example, if the column reference is (0), it returns "A:A";
/// </summary>
public override string ToString()
{
var address = ToString(Column);
return $"{address}:{address}";
}
/// <summary>
/// Converts a zero-based column index to its corresponding column letter(s) in A1 notation.
/// For example, 0 corresponds to "A", 1 to "B",
/// 26 to "AA", 27 to "AB", and so on.
/// </summary>
/// <param name="column">The zero-based column index.</param>
/// <returns>A string representing the column in A1 notation.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the column index is negative.</exception>
public static string ToString(int column)
{
if (column < 0)
{
throw new ArgumentOutOfRangeException(nameof(column), "Column index must be non-negative.");
}
var sb = StringBuilderCache.Acquire();
var start = sb.Length;
column++;
while (column > 0)
{
column--;
sb.Append((char)('A' + column % 26));
column /= 26;
}
// Digits were appended least-significant first; reverse in place.
for (int i = start, j = sb.Length - 1; i < j; i++, j--)
{
(sb[i], sb[j]) = (sb[j], sb[i]);
}
return StringBuilderCache.GetStringAndRelease(sb);
}
/// <inheritdoc/>
public override bool Equals(object? obj) => obj is ColumnRef address && Equals(address);
/// <inheritdoc/>
public override int GetHashCode() => Column.GetHashCode();
/// <summary>
/// Checks if the column reference is equal to another column reference.
/// </summary>
public static bool operator ==(ColumnRef left, ColumnRef right) => left.Equals(right);
/// <summary>
/// Checks if the column reference is not equal to another column reference.
/// </summary>
public static bool operator !=(ColumnRef left, ColumnRef right) => !left.Equals(right);
}