Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions TJC.Singleton.Tests/Mocks/Logging/MockLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.Extensions.Logging;

namespace TJC.Singleton.Tests.Mocks.Logging;

internal class MockLogger : ILogger
{
public static MockLogger Default => new();

public IDisposable? BeginScope<TState>(TState state)
where TState : notnull
=> default!;

public bool IsEnabled(LogLevel logLevel) => true;

public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
Trace.WriteLine($"[{logLevel}:{eventId}] {formatter(state, exception)}");
}
}
3 changes: 2 additions & 1 deletion TJC.Singleton.Tests/Tests/Instantiated/IsInstantiatedTest.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using TJC.Singleton.Factories;
using TJC.Singleton.Tests.Mocks.Logging;

namespace TJC.Singleton.Tests.Tests.Instantiated;

Expand All @@ -13,7 +14,7 @@ public void SingletonGetInstantiatedAfterBeingReferencedTest()
MockSingletonInstantiated.IsInstantiated,
$"{nameof(MockSingletonInstantiated)} was already instantiated"
);
SingletonFactory.InstantiateAll(trace: true);
SingletonFactory.InstantiateAll(MockLogger.Default);
Assert.IsTrue(
MockSingletonInstantiated.IsInstantiated,
$"{nameof(MockSingletonInstantiated)} is not instantiated"
Expand Down
40 changes: 21 additions & 19 deletions TJC.Singleton/Factories/SingletonFactory.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System.Diagnostics;
using Microsoft.Extensions.Logging;
using System.Reflection;
using TJC.Singleton.Helpers;

namespace TJC.Singleton.Factories;

/// <summary>
/// Factory for creating <seealso cref="SingletonBase{TDerivedClass}"/>'s.
/// </summary>
public static class SingletonFactory
{
#region Constants
Expand All @@ -17,19 +20,19 @@ private class PlaceholderSingleton : SingletonBase<PlaceholderSingleton>;
/// <summary>
/// Instantiate all singletons in the current app domain.
/// </summary>
/// <param name="trace"></param>
/// <param name="throwIfFailed"></param>
/// <param name="logger"></param>
/// <param name="logLevel"></param>
/// <exception cref="Exception"></exception>
public static void InstantiateAll(bool trace = true, bool throwIfFailed = false)
public static void InstantiateAll(ILogger? logger = null, LogLevel logLevel = LogLevel.Trace, bool throwIfFailed = false)
{
var failedToInstantiate = new List<string>();
var singletons = GetSingletonTypes();

if (trace)
Trace.WriteLine($"{singletons.Count} Singletons Found");

logger?.Log(logLevel, "{count} Singletons Found", singletons.Count);

foreach (var singleton in singletons)
if (!singleton.Instantiate(trace))
if (!singleton.Instantiate(logger, logLevel))
failedToInstantiate.Add(singleton.Name);

if (throwIfFailed && failedToInstantiate.Count > 0)
Expand Down Expand Up @@ -63,34 +66,33 @@ public static List<Type> GetSingletonTypes()
}

/// <summary>
/// Instantiates a singleton of type <seealso cref="T"/>.
/// Instantiates a singleton of type <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="trace"></param>
/// <param name="logger"></param>
/// <param name="logLevel"></param>
/// <returns></returns>
public static bool Instantiate<T>(bool trace) => Instantiate(typeof(T), trace);
public static bool Instantiate<T>(ILogger? logger = null, LogLevel logLevel = LogLevel.Trace) =>
Instantiate(typeof(T), logger, logLevel);

/// <summary>
/// Instantiates a singleton of given type.
/// </summary>
/// <param name="singleton"></param>
/// <param name="trace"></param>
/// <param name="logger"></param>
/// <param name="logLevel"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
private static bool Instantiate(this Type singleton, bool trace)
{
if (trace)
Trace.WriteLine($"[{singleton.Name}] Instantiating");
private static bool Instantiate(this Type singleton, ILogger? logger = null, LogLevel logLevel = LogLevel.Trace)
{
logger?.Log(logLevel, "[{name}] Instantiating", singleton.Name);
var instanceProp =
singleton.GetProperty(
InstanceName,
BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy
) ?? throw new Exception($"[{singleton.Name}] does not have property [{InstanceName}]");
var instanceValue = instanceProp.GetValue(singleton);
if (trace)
Trace.WriteLine(
$"[{singleton.Name}] {(instanceValue != null ? "Instantiated" : "Failed to Instantiate")}"
);
logger?.Log(logLevel, "[{name}] {result}", singleton.Name, instanceValue != null ? "Instantiated" : "Failed to Instantiate");
return instanceValue != null;
}
}
7 changes: 5 additions & 2 deletions TJC.Singleton/Helpers/SingletonConstructorHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@

namespace TJC.Singleton.Helpers;

/// <summary>
/// Helpers for constructing <seealso cref="SingletonBase{TDerivedClass}"/>.
/// </summary>
public static class SingletonConstructorHelpers
{
#region Get Singlet Constructor

/// <summary>
/// Gets the singleton constructor for the type <seealso cref="T"/>.
/// Gets the singleton constructor for the type <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
Expand Down Expand Up @@ -45,7 +48,7 @@ public static ConstructorInfo GetSingletonConstructor(Type type)
#region Check if Singleton has Valid Constructor

/// <summary>
/// Checks if a singleton of type <seealso cref="T"/> has a valid constructor.
/// Checks if a singleton of type <typeparamref name="T"/>. has a valid constructor.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
Expand Down
3 changes: 3 additions & 0 deletions TJC.Singleton/Helpers/SingletonIdentifierHelpers.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
namespace TJC.Singleton.Helpers;

/// <summary>
/// Helpers for identifying details about <seealso cref="SingletonBase{TDerivedClass}"/>.
/// </summary>
public static class SingletonIdentifierHelpers
{
/// <summary>
Expand Down
2 changes: 1 addition & 1 deletion TJC.Singleton/SingletonBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
namespace TJC.Singleton;

/// <summary>
/// Creates a single instance of <seealso cref="TDerivedClass"/> that can be accessed through the <see cref="Instance"/> property.
/// Creates a single instance of <typeparamref name="TDerivedClass"/> that can be accessed through the <see cref="Instance"/> property.
/// </summary>
/// <typeparam name="TDerivedClass"></typeparam>
/// <exception cref="InvalidSingletonConstructorException">Must have a non-public parameterless constructor.</exception>
Expand Down
6 changes: 3 additions & 3 deletions TJC.Singleton/TJC.Singleton.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,15 @@
<EmbeddedResource Include="..\LICENSE" />
</ItemGroup>
<!-- DEPENDENCIES -->
<ItemGroup>
<!-- N/A -->
</ItemGroup>
<!-- TESTS -->
<ItemGroup>
<AssemblyAttribute Include="System.Runtime.CompilerServices.InternalsVisibleToAttribute">
<_Parameter1>TJC.Singleton.Tests</_Parameter1>
</AssemblyAttribute>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.7" />
</ItemGroup>
<!-- TASKS -->
<UsingTask TaskName="ReadFileContent" TaskFactory="RoslynCodeTaskFactory" AssemblyName="Microsoft.Build.Tasks.Core">
<ParameterGroup>
Expand Down
Loading