Skip to content

Commit b2219a7

Browse files
Update README
1 parent 1694ac3 commit b2219a7

113 files changed

Lines changed: 1637 additions & 589 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7137,6 +7137,192 @@ To run the above code, the following NuGet packages must be added:
71377137

71387138
For more hints, see [this](../README.md#setup-hints) page.
71397139

7140+
## Customize the generated interface
7141+
7142+
This example shows how to place a generated contract in a dedicated Contracts namespace.
7143+
7144+
```c#
7145+
using Pure.DI;
7146+
7147+
DI.Setup(nameof(Composition))
7148+
.Bind().To<InvoiceGenerator>()
7149+
.Root<App>(nameof(App));
7150+
7151+
var composition = new Composition();
7152+
var app = composition.App;
7153+
7154+
app.InvoiceId.ShouldBe("INV-0042");
7155+
7156+
public class App(IMyInvoiceGenerator generator)
7157+
{
7158+
public string InvoiceId { get; } = generator.Format(42);
7159+
}
7160+
7161+
namespace Contracts
7162+
{
7163+
public partial interface IMyInvoiceGenerator;
7164+
7165+
[GenerateInterface(namespaceName: "Contracts", interfaceName: nameof(IMyInvoiceGenerator))]
7166+
public class InvoiceGenerator : IMyInvoiceGenerator
7167+
{
7168+
public string Format(int number) => $"INV-{number:0000}";
7169+
}
7170+
}
7171+
```
7172+
7173+
To run the above code, the following NuGet package must be added:
7174+
- [Pure.DI](https://www.nuget.org/packages/Pure.DI)
7175+
7176+
The example shows how to:
7177+
- Generate an interface into a custom namespace
7178+
- Rename the generated interface
7179+
- Keep the contract separate from implementation details
7180+
7181+
## Generate an interface from a class
7182+
7183+
This example shows how a concrete service can generate a matching interface and be consumed through Pure.DI.
7184+
7185+
```c#
7186+
using Shouldly;
7187+
using Pure.DI;
7188+
7189+
DI.Setup(nameof(Composition))
7190+
.Bind().To<EmailSender>()
7191+
.Root<App>(nameof(App));
7192+
7193+
var composition = new Composition();
7194+
var app = composition.App;
7195+
7196+
app.Provider.ShouldBe("smtp");
7197+
app.Result.ShouldBe("sent:ops@contoso.com");
7198+
7199+
public partial interface IEmailSender;
7200+
7201+
[GenerateInterface]
7202+
public class EmailSender : IEmailSender
7203+
{
7204+
public string Provider => "smtp";
7205+
7206+
public string Send(string address) => $"sent:{address}";
7207+
}
7208+
7209+
public class App(IEmailSender sender)
7210+
{
7211+
public string Provider { get; } = sender.Provider;
7212+
7213+
public string Result { get; } = sender.Send("ops@contoso.com");
7214+
}
7215+
```
7216+
7217+
To run the above code, the following NuGet packages must be added:
7218+
- [Pure.DI](https://www.nuget.org/packages/Pure.DI)
7219+
- [Shouldly](https://www.nuget.org/packages/Shouldly)
7220+
7221+
The example shows how to:
7222+
- Generate an interface from a class
7223+
- Bind the generated contract in Pure.DI
7224+
- Resolve a consumer that depends on the interface
7225+
7226+
## Generate interfaces with generics
7227+
7228+
This example shows how generic members, nullable annotations, and events are preserved in a reporting scenario.
7229+
7230+
```c#
7231+
using Shouldly;
7232+
using Pure.DI;
7233+
7234+
DI.Setup(nameof(Composition))
7235+
.Bind().To<ReportFormatter>()
7236+
.Root<App>(nameof(App));
7237+
7238+
var composition = new Composition();
7239+
var app = composition.App;
7240+
7241+
app.Formatted.ShouldBe("Order #42");
7242+
app.Title.ShouldBe("Daily Report");
7243+
7244+
public partial interface IReportFormatter;
7245+
7246+
[GenerateInterface]
7247+
public class ReportFormatter : IReportFormatter
7248+
{
7249+
public string? Title { get; set; } = "Daily Report";
7250+
7251+
public event EventHandler? Changed;
7252+
7253+
public string? Format<T>(T value)
7254+
where T : class
7255+
=> value?.ToString();
7256+
7257+
[IgnoreInterface]
7258+
public void Hidden() { }
7259+
}
7260+
7261+
public class App(IReportFormatter formatter)
7262+
{
7263+
public string Title { get; } = formatter.Title ?? string.Empty;
7264+
7265+
public string Formatted { get; } = formatter.Format(new Order(42)) ?? string.Empty;
7266+
}
7267+
7268+
public class Order(int id)
7269+
{
7270+
public override string ToString() => $"Order #{id}";
7271+
}
7272+
```
7273+
7274+
To run the above code, the following NuGet packages must be added:
7275+
- [Pure.DI](https://www.nuget.org/packages/Pure.DI)
7276+
- [Shouldly](https://www.nuget.org/packages/Shouldly)
7277+
7278+
The example shows how to:
7279+
- Generate an interface for generic members
7280+
- Preserve nullable annotations
7281+
- Preserve events and generic constraints
7282+
7283+
## Ignore members in the generated interface
7284+
7285+
This example shows how to exclude internal-only members from a generated interface.
7286+
7287+
```c#
7288+
using Shouldly;
7289+
using Pure.DI;
7290+
7291+
DI.Setup(nameof(Composition))
7292+
.Bind().To<ApiClient>()
7293+
.Root<App>(nameof(App));
7294+
7295+
var composition = new Composition();
7296+
var app = composition.App;
7297+
7298+
app.Endpoint.ShouldBe("https://api.contoso.com");
7299+
7300+
public partial interface IApiClient;
7301+
7302+
[GenerateInterface]
7303+
public class ApiClient : IApiClient
7304+
{
7305+
public string Endpoint => "https://api.contoso.com";
7306+
7307+
[IgnoreInterface]
7308+
public string GetAccessToken() => "internal-token";
7309+
}
7310+
7311+
public class App(IApiClient client)
7312+
{
7313+
public string Endpoint { get; } = client.Endpoint;
7314+
}
7315+
```
7316+
7317+
To run the above code, the following NuGet packages must be added:
7318+
- [Pure.DI](https://www.nuget.org/packages/Pure.DI)
7319+
- [Shouldly](https://www.nuget.org/packages/Shouldly)
7320+
7321+
The example shows how to:
7322+
- Mark members with IgnoreInterface
7323+
- Keep only the intended contract surface
7324+
- Use the generated interface in Pure.DI
7325+
71407326
## Composition root kinds
71417327

71427328
Demonstrates different kinds of composition roots that can be created: public methods, private partial methods, and static roots. Each kind serves different use cases for accessing composition roots with appropriate visibility and lifetime semantics.

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,11 @@ dotnet run
395395
- [OnNewInstance wildcard hint](readme/onnewinstance-wildcard-hint.md)
396396
- [ToString hint](readme/tostring-hint.md)
397397
- [Check for a root](readme/check-for-a-root.md)
398+
### Interfaces
399+
- [Customize the generated interface](readme/customize-the-generated-interface.md)
400+
- [Generate an interface from a class](readme/generate-an-interface-from-a-class.md)
401+
- [Generate interfaces with generics](readme/generate-interfaces-with-generics.md)
402+
- [Ignore members in the generated interface](readme/ignore-members-in-the-generated-interface.md)
398403
### Advanced
399404
- [Composition root kinds](readme/composition-root-kinds.md)
400405
- [Factory with thread synchronization](readme/factory-with-thread-synchronization.md)
@@ -2038,7 +2043,7 @@ AI needs to understand the situation it’s in (context). This means knowing det
20382043
| --------------- | ---- | ------ |
20392044
| [AGENTS_SMALL.md](AGENTS_SMALL.md) | 62KB | 16K |
20402045
| [AGENTS_MEDIUM.md](AGENTS_MEDIUM.md) | 106KB | 27K |
2041-
| [AGENTS.md](AGENTS.md) | 387KB | 99K |
2046+
| [AGENTS.md](AGENTS.md) | 391KB | 100K |
20422047

20432048
For different IDEs, you can use the _AGENTS.md_ file as is by simply copying it to the root directory. For use with _JetBrains Rider_ and _Junie_, please refer to [these instructions](https://www.jetbrains.com/help/junie/customize-guidelines.html). For example, you can copy any _AGENTS.md_ file into your project (using _Pure.DI_) as _.junie/guidelines.md._
20442049
## How to contribute to Pure.DI

readme/SingletonDetails.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ partial class Singleton
9191
internal Singleton(Singleton parentScope)
9292
{
9393
if (Object.ReferenceEquals(parentScope, null)) throw new ArgumentNullException(nameof(parentScope));
94-
_root = parentScope._root;
94+
_root = parentScope._root ?? parentScope;
9595
}
9696

9797
[MethodImpl(MethodImplOptions.AggressiveInlining)]

readme/accumulators.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -108,32 +108,32 @@ partial class Composition
108108
[MethodImpl(MethodImplOptions.AggressiveInlining)]
109109
get
110110
{
111-
var perBlockTelemetryRegistry1 = new TelemetryRegistry();
112-
var perBlockSqlDataSource5 = new SqlDataSource();
111+
var perBlockTelemetryRegistry3 = new TelemetryRegistry();
112+
var perBlockSqlDataSource7 = new SqlDataSource();
113113
if (_singletonNetworkDataSource64 is null)
114114
lock (_lock)
115115
if (_singletonNetworkDataSource64 is null)
116116
{
117117
NetworkDataSource _singletonNetworkDataSource64Temp;
118118
_singletonNetworkDataSource64Temp = new NetworkDataSource();
119-
perBlockTelemetryRegistry1.Add(_singletonNetworkDataSource64Temp);
119+
perBlockTelemetryRegistry3.Add(_singletonNetworkDataSource64Temp);
120120
Thread.MemoryBarrier();
121121
_singletonNetworkDataSource64 = _singletonNetworkDataSource64Temp;
122122
}
123123

124-
var transientSqlDataSource3 = new SqlDataSource();
124+
var transientSqlDataSource5 = new SqlDataSource();
125125
lock (_lock)
126126
{
127-
perBlockTelemetryRegistry1.Add(transientSqlDataSource3);
127+
perBlockTelemetryRegistry3.Add(transientSqlDataSource5);
128128
}
129129

130-
var transientDashboard2 = new Dashboard(transientSqlDataSource3, _singletonNetworkDataSource64, perBlockSqlDataSource5);
130+
var transientDashboard4 = new Dashboard(transientSqlDataSource5, _singletonNetworkDataSource64, perBlockSqlDataSource7);
131131
lock (_lock)
132132
{
133-
perBlockTelemetryRegistry1.Add(transientDashboard2);
133+
perBlockTelemetryRegistry3.Add(transientDashboard4);
134134
}
135135

136-
return (transientDashboard2, perBlockTelemetryRegistry1);
136+
return (transientDashboard4, perBlockTelemetryRegistry3);
137137
}
138138
}
139139
}

readme/async-disposable-scope.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ partial class Composition: IDisposable, IAsyncDisposable
147147
internal Composition(Composition parentScope)
148148
{
149149
if (Object.ReferenceEquals(parentScope, null)) throw new ArgumentNullException(nameof(parentScope));
150-
_root = parentScope._root;
150+
_root = parentScope._root ?? parentScope;
151151
_lock = parentScope._lock;
152152
_disposables = new object[1];
153153
}
@@ -174,13 +174,13 @@ partial class Composition: IDisposable, IAsyncDisposable
174174
[MethodImpl(MethodImplOptions.AggressiveInlining)]
175175
get
176176
{
177-
Func<Session> perBlockFunc577 = new Func<Session>(
177+
Func<Session> perBlockFunc585 = new Func<Session>(
178178
[MethodImpl(MethodImplOptions.AggressiveInlining)]
179179
() =>
180180
{
181181
return new Session(this);
182182
});
183-
return new Program(perBlockFunc577);
183+
return new Program(perBlockFunc585);
184184
}
185185
}
186186

readme/async-enumerable.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,14 +89,14 @@ partial class Composition
8989
get
9090
{
9191
[MethodImpl(MethodImplOptions.AggressiveInlining)]
92-
async IAsyncEnumerable<IHealthCheck> EnumerationOf_transientIAsyncEnumerable374()
92+
async IAsyncEnumerable<IHealthCheck> EnumerationOf_transientIAsyncEnumerable376()
9393
{
9494
yield return new MemoryCheck();
9595
yield return new ExternalServiceCheck();
9696
await Task.CompletedTask;
9797
}
9898

99-
return new HealthService(EnumerationOf_transientIAsyncEnumerable374());
99+
return new HealthService(EnumerationOf_transientIAsyncEnumerable376());
100100
}
101101
}
102102
}

readme/async-root.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -76,29 +76,29 @@ partial class Composition
7676
[MethodImpl(MethodImplOptions.AggressiveInlining)]
7777
public Task<IBackupService> GetBackupServiceAsync(CancellationToken cancellationToken)
7878
{
79-
Task<IBackupService> transientTask219;
79+
Task<IBackupService> transientTask221;
8080
// Injects an instance factory
81-
Func<IBackupService> perBlockFunc220 = new Func<IBackupService>(
81+
Func<IBackupService> perBlockFunc222 = new Func<IBackupService>(
8282
[MethodImpl(MethodImplOptions.AggressiveInlining)]
8383
() =>
8484
{
8585
return new BackupService(new FileStore());
8686
});
87-
Func<IBackupService> localFactory = perBlockFunc220;
87+
Func<IBackupService> localFactory = perBlockFunc222;
8888
// Injects a task factory creating and scheduling task objects
89-
TaskFactory<IBackupService> perBlockTaskFactory221;
89+
TaskFactory<IBackupService> perBlockTaskFactory223;
9090
CancellationToken localCancellationToken = cancellationToken;
91-
TaskCreationOptions transientTaskCreationOptions225 = TaskCreationOptions.None;
92-
TaskCreationOptions localTaskCreationOptions = transientTaskCreationOptions225;
93-
TaskContinuationOptions transientTaskContinuationOptions226 = TaskContinuationOptions.None;
94-
TaskContinuationOptions localTaskContinuationOptions = transientTaskContinuationOptions226;
95-
TaskScheduler transientTaskScheduler227 = TaskScheduler.Default;
96-
TaskScheduler localTaskScheduler = transientTaskScheduler227;
97-
perBlockTaskFactory221 = new TaskFactory<IBackupService>(localCancellationToken, localTaskCreationOptions, localTaskContinuationOptions, localTaskScheduler);
98-
TaskFactory<IBackupService> localTaskFactory = perBlockTaskFactory221;
91+
TaskCreationOptions transientTaskCreationOptions227 = TaskCreationOptions.None;
92+
TaskCreationOptions localTaskCreationOptions = transientTaskCreationOptions227;
93+
TaskContinuationOptions transientTaskContinuationOptions228 = TaskContinuationOptions.None;
94+
TaskContinuationOptions localTaskContinuationOptions = transientTaskContinuationOptions228;
95+
TaskScheduler transientTaskScheduler229 = TaskScheduler.Default;
96+
TaskScheduler localTaskScheduler = transientTaskScheduler229;
97+
perBlockTaskFactory223 = new TaskFactory<IBackupService>(localCancellationToken, localTaskCreationOptions, localTaskContinuationOptions, localTaskScheduler);
98+
TaskFactory<IBackupService> localTaskFactory = perBlockTaskFactory223;
9999
// Creates and starts a task using the instance factory
100-
transientTask219 = localTaskFactory.StartNew(localFactory);
101-
return transientTask219;
100+
transientTask221 = localTaskFactory.StartNew(localFactory);
101+
return transientTask221;
102102
}
103103
}
104104
```

readme/auto-scoped.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ partial class Composition
146146
internal Composition(Composition parentScope)
147147
{
148148
if (Object.ReferenceEquals(parentScope, null)) throw new ArgumentNullException(nameof(parentScope));
149-
_root = parentScope._root;
149+
_root = parentScope._root ?? parentScope;
150150
_lock = parentScope._lock;
151151
}
152152

@@ -155,18 +155,18 @@ partial class Composition
155155
[MethodImpl(MethodImplOptions.AggressiveInlining)]
156156
get
157157
{
158-
Func<IListeningSession> perBlockFunc585 = new Func<IListeningSession>(
158+
Func<IListeningSession> perBlockFunc593 = new Func<IListeningSession>(
159159
[MethodImpl(MethodImplOptions.AggressiveInlining)]
160160
() =>
161161
{
162-
IListeningSession transientIListeningSession586;
162+
IListeningSession transientIListeningSession594;
163163
Composition localParentScope = this;
164164
// Create a child scope so scoped services (PlaybackQueue) are unique per session.
165165
var localScope = new Composition(localParentScope);
166-
transientIListeningSession586 = localScope.Session;
167-
return transientIListeningSession586;
166+
transientIListeningSession594 = localScope.Session;
167+
return transientIListeningSession594;
168168
});
169-
return new MusicApp(perBlockFunc585);
169+
return new MusicApp(perBlockFunc593);
170170
}
171171
}
172172

0 commit comments

Comments
 (0)