Skip to content

Commit

Permalink
Rename SingletonBaseClass to SingletonBase (#4)
Browse files Browse the repository at this point in the history
* Rename SingletonBaseClass to SingletonBase
  • Loading branch information
TylerCarrol authored Jun 2, 2024
1 parent e4a4ff2 commit b6e03da
Show file tree
Hide file tree
Showing 12 changed files with 61 additions and 56 deletions.
17 changes: 11 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
[![NuGet Version and Downloads count](https://buildstats.info/nuget/TJC.Singleton)](https://www.nuget.org/packages/TJC.Singleton)

# TJC Singleton
The singleton base class ensures that *only* one **instance** of a class will exist.
This singleton base & factory can be used to instantiate all singletons at once.

## Details
- This is done in a thread-safe way using a **Lazy** implementation.
---
## [SingletonBase](./TJC.Singleton/SingletonBase.cs)
- Ensures that *only* one **instance** of a **derived class** will exist.
- This is implemented in a thread-safe way using **Lazy**.
- It also ensures that the derived class has a **private** or **protected** constructor by throwing an **exception** at run-time.

## Examples
- Please see [this mock class](./TJC.Singleton.Tests/Mocks/Valid/MockSingletonValid.cs) for an example of the intended use of the [SingletonBaseClass](./TJC.Singleton/SingletonBaseClass.cs).
### Examples
- [This mock class](./TJC.Singleton.Tests/Mocks/Valid/MockSingletonValid.cs) shows a example use-case.

---
## [SingletonFactory](./TJC.Singleton/Factories/SingletonFactory.cs)
- Can be used to instantiate **ALL** classes that derive from [SingletonBase](./TJC.Singleton/SingletonBase.cs).
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace TJC.Singleton.Tests.Mocks.InvalidConstructors;

internal class MockSingletonNoConstructor : SingletonBaseClass<MockSingletonNoConstructor>, IIdentifier
internal class MockSingletonNoConstructor : SingletonBase<MockSingletonNoConstructor>, IIdentifier
{
public Guid Id { get; } = Guid.NewGuid();
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace TJC.Singleton.Tests.Mocks.InvalidConstructors;

internal class MockSingletonPrivateConstructorWithParameters : SingletonBaseClass<MockSingletonPrivateConstructorWithParameters>, IIdentifier
internal class MockSingletonPrivateConstructorWithParameters : SingletonBase<MockSingletonPrivateConstructorWithParameters>, IIdentifier
{
#pragma warning disable IDE0051
private MockSingletonPrivateConstructorWithParameters(Guid? id = null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace TJC.Singleton.Tests.Mocks.InvalidConstructors;

internal class MockSingletonProtectedConstructorWithParameters : SingletonBaseClass<MockSingletonPrivateConstructorWithParameters>, IIdentifier
internal class MockSingletonProtectedConstructorWithParameters : SingletonBase<MockSingletonPrivateConstructorWithParameters>, IIdentifier
{
protected MockSingletonProtectedConstructorWithParameters(Guid? id = null)
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace TJC.Singleton.Tests.Mocks.InvalidConstructors;

internal class MockSingletonPublicParameterlessConstructor : SingletonBaseClass<MockSingletonPublicParameterlessConstructor>, IIdentifier
internal class MockSingletonPublicParameterlessConstructor : SingletonBase<MockSingletonPublicParameterlessConstructor>, IIdentifier
{
public MockSingletonPublicParameterlessConstructor() { }

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
namespace TJC.Singleton.Tests.Mocks.Valid;

internal class MockSingletonInstantiated : SingletonBaseClass<MockSingletonInstantiated>, IIdentifier
internal class MockSingletonInstantiated : SingletonBase<MockSingletonInstantiated>, IIdentifier
{
private MockSingletonInstantiated() { }

Expand Down
4 changes: 2 additions & 2 deletions TJC.Singleton.Tests/Mocks/Valid/MockSingletonValid.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
namespace TJC.Singleton.Tests.Mocks.Valid;

/// <summary>
/// This singleton is an example of the proper intended use of the <see cref="SingletonBaseClass{TMyClass}"/>.
/// This singleton is an example of the proper intended use of the <see cref="SingletonBase{TMyClass}"/>.
/// </summary>
internal class MockSingletonValid : SingletonBaseClass<MockSingletonValid>, IIdentifier
internal class MockSingletonValid : SingletonBase<MockSingletonValid>, IIdentifier
{
private MockSingletonValid() { }

Expand Down
2 changes: 1 addition & 1 deletion TJC.Singleton.Tests/TJC.Singleton.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
Expand Down
4 changes: 2 additions & 2 deletions TJC.Singleton/Factories/SingletonFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ public static class SingletonFactory
{
#region Constants

private const string InstanceName = nameof(SingletonBaseClass<PlaceholderSingleton>.Instance);
private const string InstanceName = nameof(SingletonBase<PlaceholderSingleton>.Instance);

private class PlaceholderSingleton : SingletonBaseClass<PlaceholderSingleton>;
private class PlaceholderSingleton : SingletonBase<PlaceholderSingleton>;

#endregion

Expand Down
2 changes: 1 addition & 1 deletion TJC.Singleton/Helpers/SingletonIdentifierHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public static bool IsConcreteSingleton(Type type)
return false;

// Ensure type derives from singleton
var openGenericType = typeof(SingletonBaseClass<>);
var openGenericType = typeof(SingletonBase<>);

var baseType = type.BaseType;
while (baseType != null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,38 +1,38 @@
using TJC.Singleton.Exceptions;
using TJC.Singleton.Helpers;

namespace TJC.Singleton;

/// <summary>
/// Creates a single instance of <seealso cref="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>
public abstract class SingletonBaseClass<TDerivedClass> where TDerivedClass : SingletonBaseClass<TDerivedClass>
{
#region Fields

private static readonly Lazy<TDerivedClass> _instance = new(CreateInstance);

#endregion

#region Properties

public static TDerivedClass Instance => _instance.Value;

public static bool IsInstantiated => _instance.IsValueCreated;

#endregion

#region Methods

private static TDerivedClass CreateInstance()
{
// Use reflection to create an instance of the derived class.
var ctor = SingletonConstructorHelpers.GetSingletonConstructor<TDerivedClass>();
return (TDerivedClass)ctor.Invoke(null) ??
throw new SingletonInitializationException($"[{typeof(TDerivedClass)}] singleton failed to initialize");
}

#endregion
using TJC.Singleton.Exceptions;
using TJC.Singleton.Helpers;

namespace TJC.Singleton;

/// <summary>
/// Creates a single instance of <seealso cref="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>
public abstract class SingletonBase<TDerivedClass> where TDerivedClass : SingletonBase<TDerivedClass>
{
#region Fields

private static readonly Lazy<TDerivedClass> _instance = new(CreateInstance);

#endregion

#region Properties

public static TDerivedClass Instance => _instance.Value;

public static bool IsInstantiated => _instance.IsValueCreated;

#endregion

#region Methods

private static TDerivedClass CreateInstance()
{
// Use reflection to create an instance of the derived class.
var ctor = SingletonConstructorHelpers.GetSingletonConstructor<TDerivedClass>();
return (TDerivedClass)ctor.Invoke(null) ??
throw new SingletonInitializationException($"[{typeof(TDerivedClass)}] singleton failed to initialize");
}

#endregion
}
4 changes: 2 additions & 2 deletions TJC.Singleton/TJC.Singleton.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Title>TJC Singleton Base Class</Title>
<Description>Singleton Base Class</Description>
<Title>TJC Singleton Base</Title>
<Description>Singleton Base &amp; Singleton Factory</Description>
<RepositoryUrl>https://github.com/TJC-Tools/TJC.Singleton</RepositoryUrl>
<PackageReadmeFile>README.md</PackageReadmeFile>
</PropertyGroup>
Expand Down

0 comments on commit b6e03da

Please sign in to comment.