Skip to content

Commit

Permalink
Changed Cache behaviour
Browse files Browse the repository at this point in the history
Items can be added to the cache dynamically at any point in time. Also the method can be invoked multiple times. Fixed a bug which would occur if a child library would also use this library.  Also updated BidirectionalDict.
  • Loading branch information
TwentyFourMinutes committed Feb 23, 2020
1 parent 4075eb3 commit fc17a05
Show file tree
Hide file tree
Showing 12 changed files with 186 additions and 156 deletions.
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Validators;
using Common.EnumStringValues;

namespace StringyEnums.Benchmarks
{
[MemoryDiagnoser]
[MemoryDiagnoser]
[Config(typeof(NoOptimizationConfig))]
public class CommonEnumStringValuesExtensionBenchmark
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Validators;
using System;
using System.Collections.Generic;
using System.Text;

namespace StringyEnums.Benchmarks
{

public class NoOptimizationConfig : ManualConfig
public class NoOptimizationConfig : ManualConfig
{
public NoOptimizationConfig()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\StringyEnums\StringyEnums.csproj" />
<PackageReference Include="BidirectionalDict" Version="1.2.0" />
</ItemGroup>

<ItemGroup>
Expand Down
10 changes: 10 additions & 0 deletions src/StringyEnums/StringyEnums.Tests/EnumCoreExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
using System.Reflection;

namespace StringyEnums.Tests
{
public static class EnumCoreExtensions
{
public static void ClearCache()
=> ((dynamic)typeof(EnumCore).GetProperty("RepresentationCache", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null)).Clear();
}
}
36 changes: 36 additions & 0 deletions src/StringyEnums/StringyEnums.Tests/EnumCoreTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using StringyEnums.Shared.Models;
using Xunit;

namespace StringyEnums.Tests
{
public class EnumCoreTests
{
[Fact]
public void IsEnumToRepresenationEqual()
{
EnumCoreExtensions.ClearCache();

EnumCore.Init(cache => cache.InitWith<TestEnum>());

Assert.Equal("Enum 4", TestEnum.EnumFour.GetRepresentation());

EnumCore.Init(cache => cache.InitWith<TestUShortEnum>());

Assert.Equal("Enum 4", TestUShortEnum.EnumFour.GetRepresentation());
}

[Fact]
public void IsRepresenationToEnumEqual()
{
EnumCoreExtensions.ClearCache();

EnumCore.Init(cache => cache.InitWith<TestEnum>());

Assert.Equal(TestEnum.EnumFour, "Enum 4".GetEnumFromRepresentation<TestEnum>());

EnumCore.Init(cache => cache.InitWith<TestUShortEnum>());

Assert.Equal(TestUShortEnum.EnumFour, "Enum 4".GetEnumFromRepresentation<TestUShortEnum>());
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
using BidirectionalDict;
using StringyEnums.Shared.Models;
using Xunit;

namespace StringyEnums.Test
namespace StringyEnums.Tests
{
public class EnumExtensionTests
{
public EnumExtensionTests()
static EnumExtensionTests()
{
EnumCore.Init(init =>
{
Expand Down
2 changes: 1 addition & 1 deletion src/StringyEnums/StringyEnums.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ VisualStudioVersion = 16.0.29521.150
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StringyEnums", "StringyEnums\StringyEnums.csproj", "{EE98D267-C0D4-43E0-BCCA-609D3B7B56B9}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StringyEnums.Tests", "StringyEnums.Test\StringyEnums.Tests.csproj", "{5FB57ACD-DE3E-49DC-8121-442EF7E38B9F}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StringyEnums.Tests", "StringyEnums.Tests\StringyEnums.Tests.csproj", "{5FB57ACD-DE3E-49DC-8121-442EF7E38B9F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StringyEnums.Benchmarks", "StringyEnums.Benchmarks\StringyEnums.Benchmarks.csproj", "{D4CE5820-15CC-4D36-86B2-F858588A16ED}"
EndProject
Expand Down
174 changes: 89 additions & 85 deletions src/StringyEnums/StringyEnums/CacheInitializer.cs
Original file line number Diff line number Diff line change
@@ -1,94 +1,98 @@
using BidirectionalDict;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reflection;

namespace StringyEnums
{
/// <summary>
/// Handles the initializing of a Cache
/// </summary>
public class CacheInitializer
{
private readonly Dictionary<Type, IReadOnlyBiDictionary<uint, string>> _tempCache;

internal CacheInitializer()
{
_tempCache = new Dictionary<Type, IReadOnlyBiDictionary<uint, string>>();
}

/// <summary>
/// Initializes the cache a given set of assemblies.
/// </summary>
/// <param name="assemblies">Assemblies which contain enums which get added to the cache.</param>
public CacheInitializer InitWith(params Assembly[] assemblies)
{
foreach (var assembly in assemblies)
{
var enumTypes = assembly.GetTypes().Where(x => x.IsEnum);

foreach (var enumType in enumTypes)
{
InitWith(enumType);
}
}

return this;
}

/// <summary>
/// Initializes the cache with a given enum.
/// </summary>
/// <typeparam name="TEnum">The enum which should be added to the cache.</typeparam>
public CacheInitializer InitWith<TEnum>() where TEnum : struct, Enum
{
InitWith(typeof(TEnum));

return this;
}

/// <summary>
/// Initializes the cache with a set of given enums.
/// </summary>
/// <param name="enums">The enums which should be added to the cache.</param>
public CacheInitializer InitWith(params Enum[] enums)
{
foreach (var enumVal in enums)
{
InitWith(enumVal);
}

return this;
}

private void InitWith(Type enumType)
{
if (!enumType.IsEnum)
throw new ArgumentException("The type provided is not of type enum", nameof(enumType));

var fields = enumType.GetFields();

var temptDict = new BiDictionary<uint, string>();

foreach (var field in fields.Where(x => x.IsStatic))
{
var attr = field.GetCustomAttribute<StringRepresentationAttribute>();

if (attr is { })
{
temptDict.TryAdd(Convert.ToUInt32(field.GetValue(enumType))!, attr.StringRepresentation);
}
}

if (temptDict.Count > 0)
{
_tempCache.Add(enumType, temptDict);
}
}

internal IReadOnlyDictionary<Type, IReadOnlyBiDictionary<uint, string>> CustructCache()
=> new ReadOnlyDictionary<Type, IReadOnlyBiDictionary<uint, string>>(_tempCache.ToDictionary(k => k.Key, v => (IReadOnlyBiDictionary<uint, string>)new ReadOnlyBiDictionary<uint, string>(v.Value)));
}
/// <summary>
/// Handles the initializing of a Cache
/// </summary>
public class CacheInitializer
{
private readonly Dictionary<Type, IReadOnlyBiDictionary<uint, string>> _tempCache;

internal CacheInitializer()
{
_tempCache = new Dictionary<Type, IReadOnlyBiDictionary<uint, string>>();
}

/// <summary>
/// Initializes the cache a given set of assemblies.
/// </summary>
/// <param name="assemblies">Assemblies which contain enums which get added to the cache.</param>
public CacheInitializer InitWith(params Assembly[] assemblies)
{
foreach (var assembly in assemblies)
{
var enumTypes = assembly.GetTypes().Where(x => x.IsEnum);

foreach (var enumType in enumTypes)
{
InitWith(enumType);
}
}

return this;
}

/// <summary>
/// Initializes the cache with a given enum.
/// </summary>
/// <typeparam name="TEnum">The enum which should be added to the cache.</typeparam>
public CacheInitializer InitWith<TEnum>() where TEnum : struct, Enum
{
InitWith(typeof(TEnum));

return this;
}

/// <summary>
/// Initializes the cache with a set of given enums.
/// </summary>
/// <param name="enums">The enums which should be added to the cache.</param>
public CacheInitializer InitWith(params Enum[] enums)
{
foreach (var enumVal in enums)
{
InitWith(enumVal);
}

return this;
}

private void InitWith(Type enumType)
{
if (!enumType.IsEnum)
throw new ArgumentException("The type provided is not of type enum", nameof(enumType));

var fields = enumType.GetFields();

var temptDict = new BiDictionary<uint, string>();

foreach (var field in fields.Where(x => x.IsStatic))
{
var attr = field.GetCustomAttribute<StringRepresentationAttribute>();

if (attr is { })
{
temptDict.TryAdd(Convert.ToUInt32(field.GetValue(enumType))!, attr.StringRepresentation);
}
}

if (temptDict.Count > 0)
{
_tempCache.Add(enumType, temptDict);
}
}

internal IEnumerable<KeyValuePair<Type, IReadOnlyBiDictionary<uint, string>>> CustructCache()
{
foreach (var cacheItem in _tempCache)
{
yield return new KeyValuePair<Type, IReadOnlyBiDictionary<uint, string>>(cacheItem.Key, new ReadOnlyBiDictionary<uint, string>(cacheItem.Value));
}
}
}
}
81 changes: 43 additions & 38 deletions src/StringyEnums/StringyEnums/EnumCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,47 @@

namespace StringyEnums
{
/// <summary>
/// Handles caching of enums which have the <see cref="StringRepresentationAttribute"/>.
/// </summary>
public static class EnumCore
{
private static IReadOnlyDictionary<Type, IReadOnlyBiDictionary<uint, string>>? _representationCache;

internal static IReadOnlyDictionary<Type, IReadOnlyBiDictionary<uint, string>> RepresentationCache
{
get => _representationCache ?? throw new NotInitializedException("Please call the EnumCore.Initialize method at application startup.");
private set => _representationCache = value;
}

/// <summary>
/// Initializes the <see cref="EnumCore"/> with the enums contained in the calling assembly.
/// </summary>
public static void Init()
{
RepresentationCache = new CacheInitializer().InitWith(Assembly.GetCallingAssembly()).CustructCache();
}

/// <summary>
/// Initializes the <see cref="EnumCore"/> with the enums provided by the <paramref name="initializer"/>.
/// </summary>
/// <param name="initializer">Provides a <see cref="CacheInitializer"/> instance where assemblies with enums can be added.</param>
/// <param name="includeCallingAssembly">Indicates whether the calling assembly should be searched for enums or not.</param>
public static void Init(Action<CacheInitializer> initializer, bool includeCallingAssembly = true)
{
var cacheInit = new CacheInitializer();

if (includeCallingAssembly)
cacheInit.InitWith(Assembly.GetCallingAssembly());

initializer?.Invoke(cacheInit);

RepresentationCache = cacheInit.CustructCache();
}
}
/// <summary>
/// Handles caching of enums which have the <see cref="StringRepresentationAttribute"/>.
/// </summary>
public static class EnumCore
{
internal static IDictionary<Type, IReadOnlyBiDictionary<uint, string>> RepresentationCache { get; }

static EnumCore()
{
RepresentationCache = new Dictionary<Type, IReadOnlyBiDictionary<uint, string>>();
}

/// <summary>
/// Initializes the <see cref="EnumCore"/> with the enums contained in the calling assembly.
/// </summary>
public static void Init()
=> InitFromCache(new CacheInitializer().InitWith(Assembly.GetCallingAssembly()));

/// <summary>
/// Initializes the <see cref="EnumCore"/> with the enums provided by the <paramref name="initializer"/>.
/// </summary>
/// <param name="initializer">Provides a <see cref="CacheInitializer"/> instance where assemblies with enums can be added.</param>
/// <param name="includeCallingAssembly">Indicates whether the calling assembly should be searched for enums or not.</param>
public static void Init(Action<CacheInitializer> initializer, bool includeCallingAssembly = true)
{
var cacheInit = new CacheInitializer();

if (includeCallingAssembly)
cacheInit.InitWith(Assembly.GetCallingAssembly());

initializer?.Invoke(cacheInit);

InitFromCache(cacheInit);
}

private static void InitFromCache(CacheInitializer cache)
{
foreach (var cacheItem in cache.CustructCache())
{
RepresentationCache.Add(cacheItem);
}
}
}
}
16 changes: 0 additions & 16 deletions src/StringyEnums/StringyEnums/NotInitializedException.cs

This file was deleted.

Loading

0 comments on commit fc17a05

Please sign in to comment.