NameHillSoftware.TypeAdoption
1.0.2
dotnet add package NameHillSoftware.TypeAdoption --version 1.0.2
NuGet\Install-Package NameHillSoftware.TypeAdoption -Version 1.0.2
<PackageReference Include="NameHillSoftware.TypeAdoption" Version="1.0.2" />
<PackageVersion Include="NameHillSoftware.TypeAdoption" Version="1.0.2" />
<PackageReference Include="NameHillSoftware.TypeAdoption" />
paket add NameHillSoftware.TypeAdoption --version 1.0.2
#r "nuget: NameHillSoftware.TypeAdoption, 1.0.2"
#:package NameHillSoftware.TypeAdoption@1.0.2
#addin nuget:?package=NameHillSoftware.TypeAdoption&version=1.0.2
#tool nuget:?package=NameHillSoftware.TypeAdoption&version=1.0.2
TypeAdoption
Adopt the implementations of members in your class and embrace delegation, composition over inheritance, and mix-in style coding in C#.
Usage
When the [Adopt] attribute is attached to a member, the parent class will automatically adopt the interface that the
member is declared as. All interface members will delegate to the adopted member, unless specified in the working class.
For example:
public partial class ConfiguredLogger<T>(ILogger<T> innerLogger)
{
[Adopt]
private readonly ILogger<T> _innerLogger = innerLogger;
public bool IsEnabled(LogLevel logLevel)
{
return logLevel > LogLevel.Debug;
}
// Other ILogger members are automatically delegated to _innerLogger.
}
And what is automatically generated:
// <auto-generated/>
#nullable restore
namespace TypeAdoption.Sample;
public partial class ConfiguredLogger<T> : Microsoft.Extensions.Logging.ILogger<T>
{
#nullable enable
public System.IDisposable? BeginScope<TState>(TState state) where TState : notnull
{
return _innerLogger.BeginScope<TState>(state);
}
#nullable restore
#nullable enable
public void Log<TState>(Microsoft.Extensions.Logging.LogLevel logLevel, Microsoft.Extensions.Logging.EventId eventId, TState state, System.Exception? exception, System.Func<TState, System.Exception?, string> formatter)
{
_innerLogger.Log<TState>(logLevel, eventId, state, exception, formatter);
}
#nullable restore
}
Advanced Usage
You can keep the adoption secret by setting the Publicly option to false, generating an explicit implementation:
public interface ILoggerConfiguration
{
string? LogTag { get; set; }
int MinLevel { get; set; }
LogLevel GetMinLevel();
}
public class Configuration : ILoggerConfiguration
{
public string? LogTag { get; set; }
public int MinLevel { get; set; }
public LogLevel GetMinLevel()
{
return (LogLevel)MinLevel;
}
}
public partial class ConfiguredLogger<T>(ILogger<T> innerLogger, IConfiguration configuration)
{
[Adopt]
private readonly ILogger<T> _innerLogger = innerLogger;
[Adopt(Publicly = false)]
private readonly IConfiguration _innerConfiguration = configuration;
public bool IsEnabled(LogLevel logLevel)
{
return logLevel >= _innerConfiguration.GetMinLevel();
}
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if (IsEnabled(logLevel))
_innerLogger.Log(logLevel, eventId, state, exception, formatter);
}
}
The automatically generated class will look like this:
// <auto-generated/>
#nullable restore
namespace TypeAdoption.Tests.Lib;
public partial class ConfiguredLogger<T> : TypeAdoption.Tests.Lib.ILoggerConfiguration
{
Microsoft.Extensions.Logging.LogLevel TypeAdoption.Tests.Lib.ILoggerConfiguration.GetMinLevel()
{
return _hiddenConfiguration.GetMinLevel();
}
#nullable enable
string? TypeAdoption.Tests.Lib.ILoggerConfiguration.LogTag { get => _hiddenConfiguration.LogTag; set => _hiddenConfiguration.LogTag = value; }
#nullable restore
int TypeAdoption.Tests.Lib.ILoggerConfiguration.MinLevel { get => _hiddenConfiguration.MinLevel; set => _hiddenConfiguration.MinLevel = value; }
}
The auto-generated logger adoption is reduced to just one method:
// <auto-generated/>
#nullable restore
namespace TypeAdoption.Sample;
public partial class ConfiguredLogger<T> : Microsoft.Extensions.Logging.ILogger<T>
{
#nullable enable
public System.IDisposable? BeginScope<TState>(TState state) where TState : notnull
{
return _innerLogger.BeginScope<TState>(state);
}
#nullable restore
}
And calling the code:
var entity = new AdoptingEntity(new HumanEntity());
using var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
var logger = new ConfiguredLogger<Program>(loggerFactory.CreateLogger<Program>(), new Configuration
{
MinLevel = LogLevel.Information,
});
// Will log
logger.LogInformation(entity.SayHello());
((ILoggerConfiguration)logger).MinLevel = LogLevel.Warning;
// Won't log
logger.LogInformation(entity.Backpack.ToString());
Notes
Normal compiler rules apply — so any conflicts, such as two interfaces with equivalent signatures, will have to be resolved in the actual class.
Development
Credit
This project was originally started as a fork of Decorator Generator. All credit to Leopoldo Fu for their well-written library, it provided an excellent base for getting started!
License
This project is licensed under the Apache License Version 2.0 - see the LICENSE file for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Microsoft.CodeAnalysis.CSharp (>= 4.3.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
1.0.2
- More documentation improvements
1.0.1
- Improved documentation
1.0.0
- Added project documentation and licensing.
0.2.0
- Support attaching [Adopt] to properties.
- Support events.
- Support nested interfaces.
0.1.0
- Initial version supporting attaching [Adopt] to members.