-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Build and deploy VitePress documentation
- Loading branch information
Showing
119 changed files
with
17,463 additions
and
0 deletions.
There are no files selected for viewing
File renamed without changes.
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
*.swp | ||
*.*~ | ||
project.lock.json | ||
.DS_Store | ||
*.pyc | ||
nupkg/ | ||
|
||
# Visual Studio Code | ||
.vscode | ||
|
||
# Rider | ||
.idea | ||
|
||
# User-specific files | ||
*.suo | ||
*.user | ||
*.userosscache | ||
*.sln.docstates | ||
|
||
# Build results | ||
[Dd]ebug/ | ||
[Dd]ebugPublic/ | ||
[Rr]elease/ | ||
[Rr]eleases/ | ||
x64/ | ||
x86/ | ||
build/ | ||
bld/ | ||
[Bb]in/ | ||
[Oo]bj/ | ||
[Oo]ut/ | ||
msbuild.log | ||
msbuild.err | ||
msbuild.wrn | ||
|
||
# Visual Studio 2015 | ||
.vs/ |
142 changes: 142 additions & 0 deletions
142
assets/notes/Csharp Design Patterns/Creational/1.Singleton.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
# Singleton | ||
|
||
## Concept | ||
|
||
Generally, `Singleton` is the only instance exists during a `AppDomain` or a `Thread`. | ||
|
||
## Basic Implementation | ||
|
||
```csharp | ||
class Singleton | ||
{ | ||
private static readonly Singleton _instance = new(); | ||
public static Singleton Instance => _instance.Value; | ||
private Singleton() { } | ||
|
||
// ...elided | ||
} | ||
``` | ||
|
||
## Lazy Initialization | ||
|
||
Lazy initialization ensures the instance won't be created until it were accessed, and `Lazy<T>` also ensures **thread safety**. | ||
Pass a delegate to `Lazy<T>.Lazy(Func<T>)` to generate its value. | ||
|
||
```csharp | ||
class Singleton | ||
{ | ||
private static readonly Lazy<Singleton> _instance = new(() => new()); | ||
public static Singleton Instance => _instance.Value; | ||
private Singleton() { } | ||
|
||
// ...elided | ||
} | ||
``` | ||
|
||
## `Singleton` is a **BAD** idea? | ||
|
||
### Testablity issue | ||
|
||
Singleton is hard to test, using Singleton of a type means hard coding it in your code base, not flexible enough. | ||
|
||
- Use `interface` to abstract singleton usage to enhance your code. | ||
|
||
### Dependency Injection | ||
|
||
The best practice is using **Dependency Injection** to generate an singleton in runtime instead of coding on your own. | ||
Each call to `container.Resolve<T>()` will return a shared instance of the specified type in the same thread. With this approach, we don't need to code our singleton on our own, but constructor of target type shall be `public`, or the **IOC** framework can not access it during the runtime. | ||
|
||
- Following example uses `Autofac` to perform dependency injection. | ||
|
||
```csharp | ||
class OrdinaryClass | ||
{ | ||
public OrdinaryClass() { } | ||
// ...elided | ||
} | ||
public class DependencyInjectionTest | ||
{ | ||
[Fact] | ||
public void DependencyInjection() | ||
{ | ||
ContainerBuilder builder = new(); | ||
builder.RegisterType<OrdinaryClass>().SingleInstance(); | ||
using var container = builder.Build(); | ||
Assert.True(container.Resolve<OrdinaryClass>() is not null); | ||
} | ||
} | ||
``` | ||
|
||
## Mono-state Singleton | ||
|
||
**Mono-state Singleton** allows you to access its constructor, but all state of this type are static, all instances shares the same states. | ||
Not a good approach though. | ||
|
||
```csharp | ||
class MonoStateSingleton | ||
{ | ||
private static int _state01 = 0x0; | ||
private static int _state02 = 0x64; | ||
|
||
public int State01 { get => _state01; set => _state01 = value; } | ||
public int State02 { get => _state02; set => _state02 = value; } | ||
|
||
// ...elided | ||
} | ||
``` | ||
|
||
## Thread Singleton | ||
|
||
Use `ThreadLocal<T>` to make object thread safe and static for each thread. | ||
Each thread has a different singleton | ||
|
||
```csharp | ||
class ThreadSingleton | ||
{ | ||
private static readonly ThreadLocal<ThreadSingleton> _instance = new(() => new()); | ||
private int _threadAccessedCount; | ||
public static ThreadSingleton Instance => _instance.Value!; | ||
public int ThreadAccessedCount => ++_threadAccessedCount; | ||
private ThreadSingleton() { } | ||
|
||
//...elided | ||
} | ||
``` | ||
|
||
## Ambient Context | ||
|
||
**Ambient Context** is similar to singleton pattern, one of its advantage is, it allows you to change the static value/state of the type during the procedure. And in some cases we don't have to preserve a parameter position for a method that access the AmbientContext type. | ||
When `IDisposable` is implemented by `AmbientContext`, `using` statement will auto invoke `Dispose()`, this can be used to restore the state depending on how you implement the `Dispose()` method. | ||
|
||
```csharp | ||
class AmbientContext : IDisposable | ||
{ | ||
private static int _currentState; | ||
public static int CurrentState { get => _currentState; private set => _currentState = value; } | ||
|
||
private static readonly ThreadLocal<AmbientContext> _context = new(() => new()); | ||
public static AmbientContext CurrentContext { get => _context.Value!; set => _context.Value = value; } | ||
public AmbientContext(int state = 250) => _currentState = state; | ||
public void Dispose() => _currentState = 250; | ||
|
||
//...elided | ||
} | ||
|
||
public class AmbientContextTest | ||
{ | ||
[Fact] | ||
public void Test() | ||
{ | ||
AmbientContext.CurrentContext = new AmbientContext(); | ||
DoSomethingUsingAmbientContext(); | ||
using (var context = new AmbientContext(500)) | ||
{ | ||
Assert.Equal(500, AmbientContext.CurrentState); | ||
} | ||
Assert.Equal(250, AmbientContext.CurrentState); | ||
|
||
static void DoSomethingUsingAmbientContext() => | ||
Console.WriteLine($"state is {AmbientContext.CurrentState}"); | ||
} | ||
} | ||
``` |
25 changes: 25 additions & 0 deletions
25
assets/notes/Csharp Design Patterns/CsharpDesignPatternsDemo/CSharpDesignPatternsDemo.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net7.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
<LangVersion>preview</LangVersion> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" /> | ||
<PackageReference Include="xunit" Version="2.4.2" /> | ||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
<PackageReference Include="coverlet.collector" Version="3.2.0"> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
<PrivateAssets>all</PrivateAssets> | ||
</PackageReference> | ||
<PackageReference Include="Autofac" Version="7.0.1" /> | ||
</ItemGroup> | ||
|
||
</Project> |
25 changes: 25 additions & 0 deletions
25
assets/notes/Csharp Design Patterns/CsharpDesignPatternsDemo/CSharpDesignPatternsDemo.sln
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
|
||
Microsoft Visual Studio Solution File, Format Version 12.00 | ||
# Visual Studio Version 17 | ||
VisualStudioVersion = 17.5.001.0 | ||
MinimumVisualStudioVersion = 10.0.40219.1 | ||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CSharpDesignPatternsDemo", "CSharpDesignPatternsDemo.csproj", "{3718B8A4-4E51-4967-B9E0-0742EE586D15}" | ||
EndProject | ||
Global | ||
GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||
Debug|Any CPU = Debug|Any CPU | ||
Release|Any CPU = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(ProjectConfigurationPlatforms) = postSolution | ||
{3718B8A4-4E51-4967-B9E0-0742EE586D15}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||
{3718B8A4-4E51-4967-B9E0-0742EE586D15}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||
{3718B8A4-4E51-4967-B9E0-0742EE586D15}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||
{3718B8A4-4E51-4967-B9E0-0742EE586D15}.Release|Any CPU.Build.0 = Release|Any CPU | ||
EndGlobalSection | ||
GlobalSection(SolutionProperties) = preSolution | ||
HideSolutionNode = FALSE | ||
EndGlobalSection | ||
GlobalSection(ExtensibilityGlobals) = postSolution | ||
SolutionGuid = {C5A099B1-B1CB-4E56-BB61-3F90AD020550} | ||
EndGlobalSection | ||
EndGlobal |
9 changes: 9 additions & 0 deletions
9
...tes/Csharp Design Patterns/CsharpDesignPatternsDemo/Creational/1.Singleton/1.Singleton.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
namespace CsharpDesignPatternsDemo.Creational.Singleton; | ||
class Singleton | ||
{ | ||
private static readonly Lazy<Singleton> _instance = new(() => new()); | ||
public static Singleton Instance => _instance.Value; | ||
private Singleton() { } | ||
|
||
// ...elided | ||
} |
20 changes: 20 additions & 0 deletions
20
... Design Patterns/CsharpDesignPatternsDemo/Creational/1.Singleton/2.DependencyInjection.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using Xunit; | ||
using Autofac; | ||
|
||
namespace CsharpDesignPatternsDemo.Creational.Singleton; | ||
class OrdinaryClass | ||
{ | ||
public OrdinaryClass() { } | ||
// ...elided | ||
} | ||
public class DependencyInjectionTest | ||
{ | ||
[Fact] | ||
public void DependencyInjection() | ||
{ | ||
ContainerBuilder builder = new(); | ||
builder.RegisterType<OrdinaryClass>().SingleInstance(); | ||
using var container = builder.Build(); | ||
Assert.True(container.Resolve<OrdinaryClass>() is not null); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
...p Design Patterns/CsharpDesignPatternsDemo/Creational/1.Singleton/3.MonoStateSingleton.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
using Xunit; | ||
|
||
namespace CsharpDesignPatternsDemo.Creational.Singleton; | ||
|
||
class MonoStateSingleton | ||
{ | ||
private static int _state01 = 0x0; | ||
private static int _state02 = 0x64; | ||
|
||
public int State01 { get => _state01; set => _state01 = value; } | ||
public int State02 { get => _state02; set => _state02 = value; } | ||
|
||
// ...elided | ||
} | ||
|
||
public class MonoStateSingletonTest | ||
{ | ||
[Fact] | ||
public void Test() | ||
{ | ||
var a = new MonoStateSingleton(); | ||
var b = new MonoStateSingleton(); | ||
Assert.True(a.State01 == b.State01 && a.State02 == b.State02); | ||
a.State01 = 0xFA; | ||
a.State02 = 0b1_1111_0100; | ||
Assert.True(a.State01 == b.State01 && a.State02 == b.State02); | ||
} | ||
} |
33 changes: 33 additions & 0 deletions
33
...harp Design Patterns/CsharpDesignPatternsDemo/Creational/1.Singleton/4.ThreadSingleton.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
namespace CsharpDesignPatternsDemo.Creational.Singleton; | ||
|
||
using Xunit; | ||
|
||
class ThreadSingleton | ||
{ | ||
private static readonly ThreadLocal<ThreadSingleton> _instance = new(() => new()); | ||
private int _threadAccessedCount; | ||
public static ThreadSingleton Instance => _instance.Value!; | ||
public int ThreadAccessedCount => ++_threadAccessedCount; | ||
private ThreadSingleton() { } | ||
|
||
// ...elided | ||
} | ||
|
||
public class ThreadSingletonTest | ||
{ | ||
[Fact] | ||
public async Task Test() | ||
{ | ||
var func = () => | ||
{ | ||
var managedThreadId = Environment.CurrentManagedThreadId; | ||
var singletonId = ThreadSingleton.Instance.ThreadAccessedCount; | ||
Console.WriteLine($"Message from thread{managedThreadId} where singleton id is {singletonId}"); | ||
return (managedThreadId, singletonId); | ||
}; | ||
var t1 = Task.Run(func); | ||
var t2 = Task.Run(func); | ||
var a = await Task.WhenAll(t1, t2); | ||
Assert.True(a[0].managedThreadId != a[1].managedThreadId && a[0].singletonId == a[1].singletonId); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
...sharp Design Patterns/CsharpDesignPatternsDemo/Creational/1.Singleton/5.AmbientContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using Xunit; | ||
namespace CsharpDesignPatternsDemo.Creational.Singleton; | ||
|
||
class AmbientContext : IDisposable | ||
{ | ||
private static int _currentState; | ||
public static int CurrentState { get => _currentState; private set => _currentState = value; } | ||
|
||
private static readonly ThreadLocal<AmbientContext> _context = new(() => new()); | ||
public static AmbientContext CurrentContext { get => _context.Value!; set => _context.Value = value; } | ||
public AmbientContext(int state = 250) => _currentState = state; | ||
public void Dispose() => _currentState = 250; | ||
|
||
//...elided | ||
} | ||
|
||
|
||
public class AmbientContextTest | ||
{ | ||
[Fact] | ||
public void Test() | ||
{ | ||
AmbientContext.CurrentContext = new AmbientContext(); | ||
DoSomethingUsingAmbientContext(); | ||
using (var context = new AmbientContext(500)) | ||
{ | ||
Assert.Equal(500, AmbientContext.CurrentState); | ||
} | ||
Assert.Equal(250, AmbientContext.CurrentState); | ||
|
||
static void DoSomethingUsingAmbientContext() => | ||
Console.WriteLine($"state is {AmbientContext.CurrentState}"); | ||
} | ||
} |
1 change: 1 addition & 0 deletions
1
assets/notes/Csharp Design Patterns/CsharpDesignPatternsDemo/Program.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
Oops, something went wrong.