Skip to content

Commit 067ec6f

Browse files
committed
Feat: Updated TypedValueStore
1 parent f9ea965 commit 067ec6f

File tree

7 files changed

+158
-9
lines changed

7 files changed

+158
-9
lines changed

CodeOfChaos.Types.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CodeOfChaos.Types.DataSeede
2020
EndProject
2121
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests.CodeOfChaos.Types.DataSeeder", "tests\Tests.CodeOfChaos.Types.DataSeeder\Tests.CodeOfChaos.Types.DataSeeder.csproj", "{228BA72E-139E-4C32-AC14-1B415921CE18}"
2222
EndProject
23+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmarks.CodeOfChaos.Types.TypedValueStore", "tests\Benchmarks.CodeOfChaos.Types.TypedValueStore\Benchmarks.CodeOfChaos.Types.TypedValueStore.csproj", "{46E31210-89A5-4E2B-8C89-4757ED54E869}"
24+
EndProject
2325
Global
2426
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2527
Debug|Any CPU = Debug|Any CPU
@@ -54,6 +56,10 @@ Global
5456
{228BA72E-139E-4C32-AC14-1B415921CE18}.Debug|Any CPU.Build.0 = Debug|Any CPU
5557
{228BA72E-139E-4C32-AC14-1B415921CE18}.Release|Any CPU.ActiveCfg = Release|Any CPU
5658
{228BA72E-139E-4C32-AC14-1B415921CE18}.Release|Any CPU.Build.0 = Release|Any CPU
59+
{46E31210-89A5-4E2B-8C89-4757ED54E869}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
60+
{46E31210-89A5-4E2B-8C89-4757ED54E869}.Debug|Any CPU.Build.0 = Debug|Any CPU
61+
{46E31210-89A5-4E2B-8C89-4757ED54E869}.Release|Any CPU.ActiveCfg = Release|Any CPU
62+
{46E31210-89A5-4E2B-8C89-4757ED54E869}.Release|Any CPU.Build.0 = Release|Any CPU
5763
EndGlobalSection
5864
GlobalSection(NestedProjects) = preSolution
5965
{26284571-0E09-4BAF-8C2B-DF87DCC1BA0B} = {8DD280D4-1E14-4D5E-AFE6-58DD8F079DCC}
@@ -63,5 +69,6 @@ Global
6369
{D98BE3BB-BDDB-416A-A9E4-8DA232D0D6A1} = {8DD280D4-1E14-4D5E-AFE6-58DD8F079DCC}
6470
{5087A810-E729-4D6F-8CC1-4969D6B1F282} = {197E72AD-DEAB-4350-AFC3-A3BB38720BF5}
6571
{228BA72E-139E-4C32-AC14-1B415921CE18} = {8DD280D4-1E14-4D5E-AFE6-58DD8F079DCC}
72+
{46E31210-89A5-4E2B-8C89-4757ED54E869} = {8DD280D4-1E14-4D5E-AFE6-58DD8F079DCC}
6673
EndGlobalSection
6774
EndGlobal

src/CodeOfChaos.Types.TypedValueStore/IValueContainer.cs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ namespace CodeOfChaos.Types;
88
// Code
99
// ---------------------------------------------------------------------------------------------------------------------
1010
public interface IValueContainer {
11+
Type UnderlyingType { get; }
12+
1113
bool TryGetAsValue<T>([NotNullWhen(true)] out T? output) where T : notnull;
12-
Type GetTypeOfValue();
14+
T GetAsValue<T>() where T : notnull;
1315
}

src/CodeOfChaos.Types.TypedValueStore/TypedValueStore.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ public bool TryGetValue<T>(TKey key, [NotNullWhen(true)] out T? value) where T :
128128
&& container.TryGetAsValue(out value);
129129
}
130130

131+
public T GetValue<T>(TKey key) where T : notnull {
132+
if (_storage.TryGetValue(key, out IValueContainer? container)) return container.GetAsValue<T>();
133+
throw new KeyNotFoundException();
134+
}
135+
131136
/// <summary>
132137
/// Determines whether the store contains a specific key.
133138
/// </summary>

src/CodeOfChaos.Types.TypedValueStore/ValueContainer.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,27 @@ namespace CodeOfChaos.Types;
88
// Code
99
// ---------------------------------------------------------------------------------------------------------------------
1010
public readonly record struct ValueContainer<T>(T Value) : IValueContainer where T : notnull {
11+
private readonly Lazy<Type> _lazyUnderlyingType = new(() => typeof(T));
12+
public Type UnderlyingType => _lazyUnderlyingType.Value;
13+
1114
public bool TryGetAsValue<T1>([NotNullWhen(true)] out T1? output) where T1 : notnull {
12-
if (Value is T1 castedValue) {
13-
output = castedValue;
14-
return true;
15+
switch (Value) {
16+
case T1 castedValue:
17+
output = castedValue;
18+
return true;
19+
default:
20+
output = default;
21+
return false;
22+
}
23+
}
24+
25+
// ReSharper disable once ConvertSwitchStatementToSwitchExpression
26+
public T1 GetAsValue<T1>() where T1 : notnull {
27+
switch (Value) {
28+
case T1 castedValue:
29+
return castedValue;
30+
default:
31+
throw new InvalidCastException($"Value of type {UnderlyingType} cannot be casted to {typeof(T1)}");
1532
}
16-
17-
output = default;
18-
return false;
1933
}
20-
21-
public Type GetTypeOfValue() => typeof(T);
2234
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net9.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
</PropertyGroup>
9+
10+
<ItemGroup>
11+
<PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
12+
</ItemGroup>
13+
14+
<ItemGroup>
15+
<ProjectReference Include="..\..\src\CodeOfChaos.Types.TypedValueStore\CodeOfChaos.Types.TypedValueStore.csproj" />
16+
</ItemGroup>
17+
18+
</Project>
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
using BenchmarkDotNet.Running;
5+
6+
namespace Benchmarks.CodeOfChaos.Types.TypedValueStore;
7+
8+
// ---------------------------------------------------------------------------------------------------------------------
9+
// Code
10+
// ---------------------------------------------------------------------------------------------------------------------
11+
public static class Program {
12+
public static void Main(string[] args) {
13+
BenchmarkRunner.Run<ValueContainerBenchmarks>();
14+
}
15+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// ---------------------------------------------------------------------------------------------------------------------
2+
// Imports
3+
// ---------------------------------------------------------------------------------------------------------------------
4+
using BenchmarkDotNet.Attributes;
5+
using System.Diagnostics.CodeAnalysis;
6+
7+
namespace Benchmarks.CodeOfChaos.Types.TypedValueStore;
8+
9+
// ---------------------------------------------------------------------------------------------------------------------
10+
// Code
11+
// ---------------------------------------------------------------------------------------------------------------------
12+
[MemoryDiagnoser]
13+
public class ValueContainerBenchmarks {
14+
private OldValueContainer<int> _oldContainer;
15+
private NewValueContainer<int> _newContainer;
16+
17+
[Params(42)] // Parametrize different values for testing
18+
public int Value { get; set; }
19+
20+
[GlobalSetup]
21+
public void Setup() {
22+
_oldContainer = new OldValueContainer<int>(Value);
23+
_newContainer = new NewValueContainer<int>(Value);
24+
}
25+
26+
[Benchmark]
27+
public bool OldValueContainer_TryGetAsValue_Int() {
28+
return _oldContainer.TryGetAsValue(out int _); // Benchmarks OldValueContainer TryGetAsValue
29+
}
30+
31+
[Benchmark]
32+
public bool NewValueContainer_TryGetAsValue_Int() {
33+
return _newContainer.TryGetAsValue(out int _); // Benchmarks NewValueContainer TryGetAsValue
34+
}
35+
36+
[Benchmark]
37+
public int OldValueContainer_TryGetAsValue_Int_Value() {
38+
_oldContainer.TryGetAsValue(out int output); // Benchmarks NewValueContainer TryGetAsValue
39+
return output;
40+
}
41+
42+
[Benchmark]
43+
public int NewValueContainer_TryGetAsValue_Int_Value() {
44+
_newContainer.TryGetAsValue(out int output); // Benchmarks NewValueContainer TryGetAsValue
45+
return output;
46+
}
47+
48+
[Benchmark]
49+
public Type OldValueContainer_GetTypeOfValue()
50+
{
51+
return _oldContainer.GetTypeOfValue(); // Benchmarks type retrieval for OldValueContainer
52+
}
53+
54+
[Benchmark]
55+
public Type NewValueContainer_GetUnderlyingType()
56+
{
57+
return _newContainer.UnderlyingType; // Benchmarks type retrieval for NewValueContainer
58+
}
59+
}
60+
61+
62+
public readonly record struct NewValueContainer<T>(T Value) {
63+
private readonly Lazy<Type> _lazyUnderlyingType = new(() => typeof(T));
64+
public Type UnderlyingType => _lazyUnderlyingType.Value;
65+
66+
public bool TryGetAsValue<T1>([NotNullWhen(true)] out T1? output) where T1 : notnull {
67+
switch (Value) {
68+
case T1 castedValue:
69+
output = castedValue;
70+
return true;
71+
default:
72+
output = default;
73+
return false;
74+
}
75+
}
76+
}
77+
78+
public readonly record struct OldValueContainer<T>(T Value) {
79+
public bool TryGetAsValue<T1>([NotNullWhen(true)] out T1? output) where T1 : notnull {
80+
if (Value is T1 castedValue) {
81+
output = castedValue;
82+
return true;
83+
}
84+
85+
output = default;
86+
return false;
87+
}
88+
89+
public Type GetTypeOfValue() => typeof(T);
90+
}

0 commit comments

Comments
 (0)