Skip to content

Commit 649148e

Browse files
The most significant changes include the update of the .editorconfig file to include new C# style preferences and to silence certain diagnostics. The target framework of the Open.Text.Benchmarks.csproj project was updated from net6.0 to net8.0. In Program.cs, the benchmark test was changed from EnumParseTests to StringConcatTests. New methods and classes were added to StringBuilderExtensions.cs, StringConcat.cs, StringConcatTests.cs, and StringBuilderHelper.cs to improve string concatenation operations.
1. The `.editorconfig` file was updated to include new C# style preferences and to silence certain diagnostics. This change will help to enforce coding style consistency across the project. 2. The target framework of the `Open.Text.Benchmarks.csproj` project was updated from `net6.0` to `net8.0`. This change will allow the project to take advantage of the latest .NET features. 3. In `Program.cs`, the line `BenchmarkRunner.Run<EnumParseTests>();` was commented out and replaced with `BenchmarkRunner.Run<StringConcatTests>();`. This change shifts the focus of the benchmarking from enum parsing to string concatenation. 4. In `StringBuilderExtensions.cs`, the `TrimEnd` method was updated and a new `Append` method was added that appends the characters from another `StringBuilder` instance. These changes enhance the functionality of the `StringBuilderExtensions` class. 5. In `StringConcat.cs`, the implicit operator method was updated to return `value?.ToString();` instead of `value?.ToString()!;`. This change makes the method more null-safe. 6. A new file `StringConcatTests.cs` was added, which includes a new `StringConcatTests` class for benchmarking different string concatenation methods. This addition provides a way to measure the performance of various string concatenation techniques. 7. A new file `StringBuilderHelper.cs` was added, which includes a new `StringBuilderHelper` class that provides various operator overloads for appending characters to a `StringBuilder` instance. This addition enhances the functionality of the `StringBuilder` class.
1 parent 230c639 commit 649148e

7 files changed

Lines changed: 288 additions & 7 deletions

File tree

.editorconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,9 @@ csharp_style_prefer_utf8_string_literals = true:suggestion
223223
csharp_style_prefer_primary_constructors = true:suggestion
224224
dotnet_diagnostic.SYSLIB1045.severity = silent
225225

226+
# IDE0055: Fix formatting
227+
dotnet_diagnostic.IDE0055.severity = silent
228+
226229
[*.{cs,vb}]
227230
dotnet_style_operator_placement_when_wrapping = beginning_of_line
228231
tab_width = 4

Benchmarks/Open.Text.Benchmarks.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net6.0</TargetFramework>
5+
<TargetFramework>net8.0</TargetFramework>
66
<ImplicitUsings>enable</ImplicitUsings>
77
<Nullable>enable</Nullable>
88
</PropertyGroup>

Benchmarks/Program.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
using BenchmarkDotNet.Running;
22
using Open.Text.Benchmarks;
33

4-
BenchmarkRunner.Run<EnumParseTests>();
4+
//BenchmarkRunner.Run<EnumParseTests>();
55
//enchmarkRunner.Run<EnumToStringTests>();
66
//BenchmarkRunner.Run<CharAssumptionTests>();
77
//BenchmarkRunner.Run<EnumAttributeTests>();
88
//BenchmarkRunner.Run<IsDefinedTests>();
9+
BenchmarkRunner.Run<StringConcatTests>();

Benchmarks/StringConcatTests.cs

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
using BenchmarkDotNet.Attributes;
2+
using Microsoft.Extensions.Primitives;
3+
using System.Text;
4+
using System.Diagnostics.CodeAnalysis;
5+
6+
namespace Open.Text.Benchmarks;
7+
8+
[MemoryDiagnoser]
9+
[SuppressMessage("Performance", "CA1822:Mark members as static")]
10+
public class StringConcatTests
11+
{
12+
public static readonly string Phrase
13+
= "Hello I'm from andromeda. Take me to your leader. There is nothing to fear.";
14+
15+
private static readonly ReadOnlyMemory<string> Words = Phrase.Split(' ');
16+
17+
private static readonly ReadOnlyMemory<StringSegment> Segments = Phrase.SplitAsSegments(' ').ToArray();
18+
19+
[Benchmark(Baseline = true)]
20+
public string StringOperator()
21+
{
22+
var s = "";
23+
{
24+
var len = Words.Length;
25+
var span = Words.Span;
26+
for (var i = 0; i < len; i++)
27+
s += span[i];
28+
}
29+
30+
{
31+
var len = Segments.Length;
32+
var span = Segments.Span;
33+
for (var i = 0; i < len; i++)
34+
s += span[i].ToString();
35+
}
36+
37+
return s;
38+
}
39+
40+
[Benchmark]
41+
public string StringBuilder()
42+
{
43+
var sb = new StringBuilder();
44+
{
45+
var len = Words.Length;
46+
var span = Words.Span;
47+
for (var i = 0; i < len; i++)
48+
sb.Append(span[i]);
49+
}
50+
51+
{
52+
var len = Segments.Length;
53+
var span = Segments.Span;
54+
for (var i = 0; i < len; i++)
55+
sb.AppendSegment(span[i]);
56+
}
57+
58+
return sb.ToString();
59+
}
60+
61+
[Benchmark]
62+
public string StringBuilderHelper()
63+
{
64+
StringBuilderHelper s = "";
65+
{
66+
var len = Words.Length;
67+
var span = Words.Span;
68+
for (var i = 0; i < len; i++)
69+
s += span[i];
70+
}
71+
72+
{
73+
var len = Segments.Length;
74+
var span = Segments.Span;
75+
for (var i = 0; i < len; i++)
76+
s += span[i];
77+
}
78+
79+
return s;
80+
}
81+
82+
[Benchmark]
83+
public string StringConcat()
84+
{
85+
StringConcat s = "";
86+
{
87+
var len = Words.Length;
88+
var span = Words.Span;
89+
for (var i = 0; i < len; i++)
90+
s += span[i];
91+
}
92+
93+
{
94+
var len = Segments.Length;
95+
var span = Segments.Span;
96+
for (var i = 0; i < len; i++)
97+
s += span[i];
98+
}
99+
100+
return s;
101+
}
102+
}

Source/StringBuilderExtensions.cs

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -435,13 +435,26 @@ public static StringBuilder Append(this StringBuilder target, ReadOnlySpan<char>
435435
foreach (var v in value) target.Append(v);
436436
return target;
437437
}
438-
#endif
439438

440439
/// <summary>
441-
/// Trims whitespace from the end of the <see cref="StringBuilder"/>.
440+
/// Appends the characters from another <see cref="StringBuilder"/> this instance.
442441
/// </summary>
443-
/// <exception cref="ArgumentNullException">If <paramref name="sb"/> is null.</exception>
444-
public static StringBuilder TrimEnd(this StringBuilder sb)
442+
public static StringBuilder Append(this StringBuilder target, StringBuilder value)
443+
{
444+
if (target is null) throw new ArgumentNullException(nameof(target));
445+
if (value is null) return target;
446+
var len = value.Length;
447+
for(var i = 0;i<len;i++)
448+
target.Append(value[i]);
449+
return target;
450+
}
451+
#endif
452+
453+
/// <summary>
454+
/// Trims whitespace from the end of the <see cref="StringBuilder"/>.
455+
/// </summary>
456+
/// <exception cref="ArgumentNullException">If <paramref name="sb"/> is null.</exception>
457+
public static StringBuilder TrimEnd(this StringBuilder sb)
445458
{
446459
if (sb is null) throw new ArgumentNullException(nameof(sb));
447460
Contract.EndContractBlock();

Source/StringBuilderHelper.cs

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
using Microsoft.Extensions.Primitives;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Diagnostics.CodeAnalysis;
5+
using System.Text;
6+
7+
namespace Open.Text;
8+
9+
/// <summary>
10+
/// Allows for easy conversion from a string to a <see cref="StringBuilder"/> by declaring the type as <see cref="StringBuilderHelper"/>.
11+
/// </summary>
12+
13+
[SuppressMessage("Usage", "CA2225:Operator overloads have named alternates", Justification = "<Pending>")]
14+
public class StringBuilderHelper(StringBuilder? sb = default)
15+
{
16+
/// <summary>
17+
/// The underlying <see cref="Builder"/>.
18+
/// </summary>
19+
public StringBuilder Builder { get; } = sb ?? new();
20+
21+
/// <summary>
22+
/// Constructs a new <see cref="StringBuilderHelper"/> with a <see cref="StringBuilder"/> of the <paramref name="initialCapacity"/>.
23+
/// </summary>
24+
public StringBuilderHelper(int initialCapacity)
25+
: this(new StringBuilder(initialCapacity)) { }
26+
27+
/// <summary>
28+
/// Appends the characters to the underlying <see cref="StringBuilder"/>.
29+
/// </summary>
30+
public static StringBuilderHelper Add(StringBuilderHelper helper, string characters)
31+
{
32+
if (helper is null) throw new ArgumentNullException(nameof(helper));
33+
helper.Builder.Append(characters);
34+
return helper;
35+
}
36+
37+
/// <inheritdoc cref="Add(StringBuilderHelper, string)"/>
38+
public static StringBuilderHelper operator +(StringBuilderHelper helper, string characters)
39+
=> Add(helper, characters);
40+
41+
/// <inheritdoc cref="Add(StringBuilderHelper, string)"/>
42+
public static StringBuilderHelper operator +(StringBuilderHelper helper, StringSegment characters)
43+
{
44+
if (helper is null) throw new ArgumentNullException(nameof(helper));
45+
helper.Builder.AppendSegment(characters);
46+
return helper;
47+
}
48+
49+
/// <inheritdoc cref="Add(StringBuilderHelper, string)"/>
50+
public static StringBuilderHelper operator +(StringBuilderHelper helper, ReadOnlySpan<char> characters)
51+
{
52+
if (helper is null) throw new ArgumentNullException(nameof(helper));
53+
helper.Builder.Append(characters);
54+
return helper;
55+
}
56+
57+
/// <inheritdoc cref="Add(StringBuilderHelper, string)"/>
58+
public static StringBuilderHelper operator +(StringBuilderHelper helper, Span<char> characters)
59+
{
60+
if (helper is null) throw new ArgumentNullException(nameof(helper));
61+
helper.Builder.Append(characters);
62+
return helper;
63+
}
64+
65+
/// <inheritdoc cref="Add(StringBuilderHelper, string)"/>
66+
public static StringBuilderHelper operator +(StringBuilderHelper helper, char[] characters)
67+
{
68+
if (helper is null) throw new ArgumentNullException(nameof(helper));
69+
if (characters is null) return helper;
70+
helper.Builder.Append(characters.AsSpan());
71+
return helper;
72+
}
73+
74+
/// <inheritdoc cref="Add(StringBuilderHelper, string)"/>
75+
public static StringBuilderHelper operator +(StringBuilderHelper helper, ReadOnlyMemory<char> characters)
76+
{
77+
if (helper is null) throw new ArgumentNullException(nameof(helper));
78+
helper.Builder.Append(characters.Span);
79+
return helper;
80+
}
81+
82+
/// <inheritdoc cref="Add(StringBuilderHelper, string)"/>
83+
public static StringBuilderHelper operator +(StringBuilderHelper helper, Memory<char> characters)
84+
{
85+
if (helper is null) throw new ArgumentNullException(nameof(helper));
86+
helper.Builder.Append(characters.Span);
87+
return helper;
88+
}
89+
90+
/// <inheritdoc cref="Add(StringBuilderHelper, string)"/>
91+
public static StringBuilderHelper operator +(StringBuilderHelper helper, ArraySegment<char> characters)
92+
{
93+
if (helper is null) throw new ArgumentNullException(nameof(helper));
94+
helper.Builder.Append(characters.AsSpan());
95+
return helper;
96+
}
97+
98+
/// <inheritdoc cref="Add(StringBuilderHelper, string)"/>
99+
public static StringBuilderHelper operator +(StringBuilderHelper helper, IEnumerable<char> characters)
100+
{
101+
if (helper is null) throw new ArgumentNullException(nameof(helper));
102+
if (characters is null) return helper;
103+
var sb = helper.Builder;
104+
foreach(var c in characters)
105+
sb.Append(c);
106+
return helper;
107+
}
108+
109+
110+
/// <summary>
111+
/// Creates a new <see cref="StringBuilderHelper"/> instance beginning with the specified <paramref name="sequence"/>.
112+
/// </summary>
113+
internal static StringBuilderHelper NewFrom<T>(T sequence)
114+
=> throw new NotImplementedException();
115+
116+
/// <inheritdoc cref="NewFrom{T}(T)"/>
117+
[ExcludeFromCodeCoverage]
118+
public static implicit operator StringBuilderHelper(string sequence)
119+
=> string.IsNullOrEmpty(sequence) ? new() : new(new StringBuilder(sequence));
120+
121+
/// <inheritdoc cref="NewFrom{T}(T)"/>
122+
public static implicit operator StringBuilderHelper(char value)
123+
=> new(new StringBuilder(1).Append(value));
124+
125+
/// <inheritdoc cref="NewFrom{T}(T)"/>
126+
public static implicit operator StringBuilderHelper(StringSegment value)
127+
=> new(new StringBuilder(value.Length).AppendSegment(value));
128+
129+
/// <inheritdoc cref="NewFrom{T}(T)"/>
130+
public static implicit operator StringBuilderHelper(ReadOnlySpan<char> value)
131+
=> new(new StringBuilder(value.Length).Append(value));
132+
133+
/// <inheritdoc cref="NewFrom{T}(T)"/>
134+
public static implicit operator StringBuilderHelper(ReadOnlyMemory<char> value)
135+
=> new(new StringBuilder(value.Length).Append(value.Span));
136+
137+
/// <inheritdoc cref="NewFrom{T}(T)"/>
138+
public static implicit operator StringBuilderHelper(Memory<char> value)
139+
=> new(new StringBuilder(value.Length).Append(value.Span));
140+
141+
/// <inheritdoc cref="NewFrom{T}(T)"/>
142+
public static implicit operator StringBuilderHelper(ArraySegment<char> value)
143+
=> new(new StringBuilder(value.Count).Append(value.AsSpan()));
144+
145+
/// <inheritdoc cref="NewFrom{T}(T)"/>
146+
public static implicit operator StringBuilderHelper(char[] value)
147+
=> value is null ? new() : new(new StringBuilder(value.Length).Append(value.AsSpan()));
148+
149+
/// <inheritdoc cref="NewFrom{T}(T)"/>
150+
public static implicit operator StringBuilderHelper(StringBuilder value)
151+
=> value is null ? new() : new(new StringBuilder(value.Length).Append(value));
152+
153+
/// <summary>
154+
/// Converts the <see cref="StringConcat"/> instance to a string.
155+
/// </summary>
156+
#if NETSTANDARD2_0
157+
#else
158+
[return:NotNullIfNotNull(nameof(value))]
159+
#endif
160+
public static implicit operator string?(StringBuilderHelper? value)
161+
=> value?.Builder.ToString();
162+
}

Source/StringConcat.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,5 +211,5 @@ public static implicit operator StringConcat(StringBuilder value)
211211
[return:NotNullIfNotNull(nameof(value))]
212212
#endif
213213
public static implicit operator string?(StringConcat? value)
214-
=> value?.ToString()!;
214+
=> value?.ToString();
215215
}

0 commit comments

Comments
 (0)