InterfaceFillerCodeGen 3.1.0-rc02
See the version list below for details.
dotnet add package InterfaceFillerCodeGen --version 3.1.0-rc02
NuGet\Install-Package InterfaceFillerCodeGen -Version 3.1.0-rc02
<PackageReference Include="InterfaceFillerCodeGen" Version="3.1.0-rc02" />
paket add InterfaceFillerCodeGen --version 3.1.0-rc02
#r "nuget: InterfaceFillerCodeGen, 3.1.0-rc02"
// Install InterfaceFillerCodeGen as a Cake Addin #addin nuget:?package=InterfaceFillerCodeGen&version=3.1.0-rc02&prerelease // Install InterfaceFillerCodeGen as a Cake Tool #tool nuget:?package=InterfaceFillerCodeGen&version=3.1.0-rc02&prerelease
InterfaceFiller - Explicit Aspect Oriented Programming (AOP) in C#
The most simple but full-feature AOP framework in .NET C# by:
- Minimal and concise. All junk code are:
- Two parameter-less attributes
- Support C# built-in Caller attributes
- Explicit. No reflection in hell.
- Flexible. wysiwyg
Within minutes reading, all magic is revealed by Find all References.
Table of content:
- 1. InterfaceFiller attribute
- 2. Wrapper attribute
- 3. Wrapper With Parameter
- 4. Wrapper Resolution
- 5. Wrapper (with parameter) for
Task
method - 6. Wrapper (with parameter) for
Task<TResult>
method - 7. C# Caller Attributes
- 8. CallerParamByName and CallerParamByIndex Attribute
- 9. Reuse your Wrapper logic
- Void method
- Issue Report
Changelog
[3.1.0] - 2023-12-04
Added
- Reuse your Wrapper logic
[3.0.1] - 2023-11-22
Added
- CallerParamByName and CallerParamByIndex Attribute
Updated
- Nuget package model but backward compatible
[2.0.1] - 2023-08-28 - Breaking Change to version 1.1.x
Added
- Support C# Caller Attributes
Deprecated
- [string methodName]
[1.1.0] - 2023-08-10
Added
- [string methodName]
Specification
1. InterfaceFiller attribute
public interface ITestApi
{
int FunA(int x, int y);
Task<StreamContent> FunB(Barrier barrier, Random randomAccess);
}
The (must be) partial TestApi
class, contains testApi
backup field (ITestApi
type).
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
}
TestApi
has no implementation for interface ITestApi
but no compiler error
because InterfaceFiller
attribute marks to auto-generate the default implementation.
// <auto-generated />
partial class TestApi : ITestApi
{
public int FunA(int x, int y) => testApi.FunA(x, y);
public System.Threading.Tasks.Task<System.Net.Http.StreamContent> FunB(System.Threading.Barrier barrier, System.Random randomAccess) => testApi.FunB(barrier, randomAccess);
}
Note
- Class must be
partial
- Class must have backup-field which field type is the interface type.
- Backup-field has
[InterfaceFiller]
attribute. - If there is an implemention for a method, code-gen will skips for this method only.
2. Wrapper attribute
'Aspect' your interface with custom behavior before and/or after execution.
2.1 Normal method
Given the interface
public interface ITestApi
{
int FunA(int x);
string FunB(int x, string y);
}
and the class
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
[Wrapper]
private T Wrapper<T>(Func<T> next)
{
// Logging code here...
var r = next();
// More logging code here...
return r;
}
}
It will generate the interface.
// <auto-generated />
partial class TestApi : ITestApi
{
public int FunA(int x)
{
Func<int> func = () => testApi.FunA(x);
return Wrapper(func);
}
public string FunB(int x, string y)
{
Func<string> func = () => testApi.FunB(x, y);
return Wrapper(func);
}
}
Wrapper method:
- Having
[Wrapper]
attribute - Signature:
T Wrapper<T>(Func<T> next)
- Must be Generic method
- The generic type must be method generic type
2.2 Task
method
Given the interface
public interface ITestApi
{
Task FunA(int x);
Task FunB(int x, string y);
}
and the class
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
[Wrapper]
private async Task Wrapper(Func<Task> next)
{
// Logging code here...
await next();
// More Logging code here...
}
}
It will generate the interface implementation using back-up field and wapper method.
// <auto-generated />
partial class TestApi : ITestApi
{
public async System.Threading.Tasks.Task FunA(int x)
{
Func<System.Threading.Tasks.Task> func = () => testApi.FunA(x);
await Wrapper(func);
}
public async System.Threading.Tasks.Task FunB(int x, string y)
{
Func<System.Threading.Tasks.Task> func = () => testApi.FunB(x, y);
await Wrapper(func);
}
}
Wrapper for Task
method:
- Having
[Wrapper]
attribute - Signature:
async Task Wrapper(Func<Task> next)
2.3 Task<TResult>
method
Given the interface
public interface ITestApi
{
Task<int> FunA(int x);
Task<string> FunB(int x, string y);
}
and the class
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
[Wrapper]
private async Task<T> Wrapper<T>(Func<Task<T>> next)
{
// Logging code here...
var r = await next();
// More Logging code here...
return r;
}
}
It will generate the interface implementation.
// <auto-generated />
partial class TestApi : ITestApi
{
public async System.Threading.Tasks.Task<int> FunA(int x)
{
Func<System.Threading.Tasks.Task<int>> func = () => testApi.FunA(x);
return await Wrapper(func);
}
public async System.Threading.Tasks.Task<string> FunB(int x, string y)
{
Func<System.Threading.Tasks.Task<string>> func = () => testApi.FunB(x, y);
return await Wrapper(func);
}
}
Wrapper method:
- Having
[Wrapper]
attribute - Signature:
async Task<T> Wrapper<T>(Func<Task<T>> next)
- Must be Generic method
- The generic type must be method generic type
3. Wrapper With Parameter
3.1 Single param
Concern your 'Aspect' only.
Given the interface
public interface ITestApi
{
int FunA(int role);
string FunB(int role, string y);
}
and the class
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
[Wrapper]
private T Wrapper<T>(int role, Func<int, T> next)
{
// If role is VIP then call manager for permission
var r = next(role);
// More logging code here...
return r;
}
}
It will generate the interface implementation using back-up field and wapper method.
// <auto-generated />
partial class TestApi : ITestApi
{
public int FunA(int role)
{
Func<int, int> func = (__role) => testApi.FunA(__role);
return Wrapper(role, func);
}
public string FunB(int role, string y)
{
Func<int, string> func = (__role) => testApi.FunB(__role, y);
return Wrapper(role, func);
}
}
Wrapper method:
- Having
[Wrapper]
attribute - Signature:
T Wrapper<T>(int role, Func<int, T> next)
- All parameters before the last param (
Func<int, T> next
) must be match exactly within parameters of each interface methods. If not, default implementation is used. E.g.int userRole
ordouble role
will not be match. - Last param signature
Func<int, T> next)
. - Input in last param
int
must match withint role
- All parameters before the last param (
- Must be Generic method
- The generic type must be method generic type
3.2 Too many param
Concern your 'Aspect' only.
Given the interface
public interface ITestApi
{
int FunA(int role);
string FunB(int role, string y);
}
and the class
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
[Wrapper]
private T Wrapper<T>(int role, string name, Func<int, string, T> next)
{
// If role is VIP then call manager for permission
var r = next(role, name);
// More logging code here...
return r;
}
}
It will generate the interface implementation using default not wrapper.
// <auto-generated />
partial class TestApi : ITestApi
{
public int FunA(int role)
=> testApi.FunA(role);
public string FunB(int role, string y)
=> testApi.FunB(role, y);
}
Wrapper method:
string name
param in wrapper method match NO param in all interface methods.
4. Wrapper Resolution
4.1 Exactly match
Given the interface
public interface ITestApi
{
int FunA(int role);
string FunB(int role, string y);
}
and the class
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
[Wrapper]
private T Wrapper1<T>(int role, Func<int, T> next)
{
// If role is VIP then call manager for permission
var r = next(role);
// More logging code here...
return r;
}
[Wrapper]
private T Wrapper2<T>(int role, string y, Func<int, string, T> next)
{
// If role is VIP then call manager for permission
var r = next(role, y);
// More logging code here...
return r;
}
}
It will generate the interface implementation.
Wrapper1
→FunA
Wrapper2
→FunB
// <auto-generated />
partial class TestApi : ITestApi
{
public int FunA(int role)
{
Func<int, int> func = (__role) => testApi.FunA(__role);
return Wrapper1(role, func);
}
public string FunB
(int role, string y)
{
Func<int, string, string> func = (__role, __y) => testApi.FunB(__role, __y);
return Wrapper2(role, y, func);
}
}
4.2 More params - higher precedence
Given the interface
public interface ITestApi
{
int FunA(int role, string name, DateTime dob, decimal amount);
}
and the class
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
[Wrapper]
private T Wrapper1<T>(int role, Func<int, T> next)
{
// If role is VIP then call manager for permission
var r = next(role);
// More logging code here...
return r;
}
[Wrapper]
private T Wrapper2<T>(int role, string name, Func<int, string, T> next)
{
// If role is VIP then call manager for permission
var r = next(role, name);
// More logging code here...
return r;
}
}
It will generate the interface implementation.
Wrapper2
→FunA
Wrapper2
is higher precedence because it covers more aspect (params) ofFunA
thanWrapper1
// <auto-generated />
partial class TestApi : ITestApi
{
public int FunA(int role, string name, System.DateTime dob, decimal amount)
{
Func<int, string, int> func = (__role, __name) => testApi.FunA(__role, __name, dob, amount);
return Wrapper2(role, name, func);
}
}
4.2 Equal params - compile error
Given the interface
public interface ITestApi
{
int FunA(int role, string name, DateTime dob, decimal amount);
}
and the class
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
[Wrapper]
private T Wrapper1<T>(int role, string name, Func<int, string, T> next)
{
// If role is VIP then call manager for permission
var r = next(role, name);
// More logging code here...
return r;
}
[Wrapper]
private T Wrapper2<T>(int role, DateTime dob, Func<int, DateTime, T> next)
{
// If role is VIP then call manager for permission
var r = next(role, dob);
// More logging code here...
return r;
}
}
It will generate junk text to fail compiler
because both Wrapper1
and Wrapper2
cover equally 2 params of FunA
// <auto-generated />
partial class TestApi : ITestApi
{
mutipleWrapper
//Error: WrapperDoc.ITest.FunA(int, string, System.DateTime, decimal) has mutiple wrapper: WrapperDoc.Test.Wrapper1<T>(int, string, System.Func<int, string, T>), WrapperDoc.Test.Wrapper2<T>(int, System.DateTime, System.Func<int, System.DateTime, T>)
}
5. Wrapper (with parameter) for Task
method
Signature: async Task Wrapper(int a, Func<int, Task> next)
6. Wrapper (with parameter) for Task<TResult>
method
Signature: async Task<T> Wrapper<T>(int a, Func<int, Task<T>> next)
7. C# Caller Attributes
Support C# buit-in caller attributes
public interface ITestApi
{
int FunA(int role, string name, DateTime dob, decimal amount);
}
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
[Wrapper]
private T Wrapper1<T>(int role, string name, Func<int, string, T> next, [CallerMemberName] string memberName = "", [CallerLineNumber] int line = 0, [CallerArgumentExpression("next")] string exp = "", [CallerFilePath] string sourceFilePath = "")
{
// If role is VIP then call manager for permission
var r = next(role, name);
// More logging code here...
return r;
}
}
8. CallerParamByName and CallerParamByIndex Attribute
- Apply attribute [CallerParamByName("paramName")] or [CallerParamByIndex(1,true)] to wrapper method param to match with inteface method param
- [CallerParamByName("paramName")] will match by paramName
- [CallerParamByIndex(1,true)] will match by appling index value to inteface method param list
- The wrapper method param must have default value
- If no matching then default value is used
- The type of inteface param and the type of wrapper param:
- Equals then inteface param is passed to wrapper param
- Converible then inteface param is converted and passed to wrapper param
- Not convertible then default value is used
Note: Should NOT update/modify the value matched param. It causes side effect if it is reference object.
public class EncrytionArth
{
public string Pseed;
}
public class S15 : EncrytionArth
{
public string Title;
}
public interface ITestApi
{
int FunA(int idm, EncrytionArth strat, string name, DateTime dob, decimal amount);
int FunB(S15 strat, string hasCode);
}
public partial class TestApi : ITestApi
{
[InterfaceFiller]
private ITestApi testApi;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
}
[Wrapper]
private T Wrapper1<T>(Func<T> next, [CallerParamByName("strat")] EncrytionArth arth = default)
{
// this will match both FunA and FunB
Log.Info(arth.Pseed);
var r = next();
// More logging code here...
return r;
}
}
9. Reuse your Wrapper logic
- Move your Wrapper methods to other class.
- Decorate it's methods with [Wrapper] attribute and make them public.
- Create the instance of it in the using class
- Add the variable name to [InterfaceFiller]
public interface ITestApi
{
int FunA(int idm, EncrytionArth strat, string name, DateTime dob, decimal amount);
int FunB(S15 strat, string hasCode);
}
public class WrapperLogic
{
[Wrapper]
public T Wrapper1<T>(Func<T> next, [CallerParamByName("strat")] EncrytionArth arth = default)
{
Log.Info("WrapperLogic");
return next();
}
}
public partial class TestApi : ITestApi
{
[InterfaceFiller(nameof(wrapperLogic))]
private ITestApi testApi;
private WrapperLogic wrapperLogic;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
wrapperLogic = new WrapperLogic();
}
}
9.1 Combine a wrapper object with class own wrapper methods
public interface ITestApi
{
int FunA(int idm, EncrytionArth strat, string name, DateTime dob, decimal amount);
int FunB(S15 strat, string hasCode);
}
public class WrapperLogic
{
[Wrapper]
public T Wrapper1<T>(Func<T> next, [CallerParamByName("strat")] EncrytionArth arth = default)
{
Log.Info("WrapperLogic message");
return next();
}
}
public partial class TestApi : ITestApi
{
[InterfaceFiller(nameof(wrapperLogic))]
private ITestApi testApi;
private WrapperLogic wrapperLogic;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
wrapperLogic = new WrapperLogic();
}
[Wrapper]
public T Wrapper1<T>(Func<T> next, [CallerParamByName("strat")] EncrytionArth arth = default)
{
Log.Info("Own class wrapper");
return next();
}
}
- The calling order: WrapperLogic object → class own wrapper methods
- Log result is:
- WrapperLogic message
- Own class wrapper
9.2 Combine mutiple wrapper objects with class own wrapper methods
public interface ITestApi
{
int FunA(int idm, EncrytionArth strat, string name, DateTime dob, decimal amount);
int FunB(S15 strat, string hasCode);
}
public class WrapperLogging
{
[Wrapper]
public T Wrapper<T>(Func<T> next, [CallerParamByName("strat")] EncrytionArth arth = default)
{
Log.Info("WrapperLogging message");
return next();
}
}
public class WrapperTiming
{
[Wrapper]
public T Wrapper<T>(Func<T> next, [CallerParamByName("strat")] EncrytionArth arth = default)
{
Log.Info("WrapperTiming message");
return next();
}
}
public partial class TestApi : ITestApi
{
[InterfaceFiller(nameof(wrapperLogging), nameof(wrapperTiming))]
private ITestApi testApi;
private WrapperLogging wrapperLogging;
private WrapperTiming wrapperTiming;
public TestApi(ITestApi testApi)
{
this.testApi = testApi;
wrapperLogging = new WrapperLogging();
wrapperTiming = new WrapperTiming();
}
[Wrapper]
public T Wrapper<T>(Func<T> next, [CallerParamByName("strat")] EncrytionArth arth = default)
{
Log.Info("Own class wrapper");
return next();
}
}
- The calling order (right to left): WrapperTiming → WrapperLogging → class own wrapper methods
- Log result is:
- WrapperTiming message
- WrapperLogging message
- Own class wrapper
Void method
Void (Unsupport)
Issue Report
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. |
.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
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
5.0.0 | 132 | 3/23/2024 |
5.0.0-rc02 | 93 | 3/9/2024 |
5.0.0-rc01 | 89 | 3/9/2024 |
3.1.0 | 216 | 12/7/2023 |
3.1.0-rc03 | 121 | 12/5/2023 |
3.1.0-rc02 | 118 | 12/5/2023 |
3.1.0-rc01 | 119 | 12/4/2023 |
3.0.1-rc02 | 101 | 11/22/2023 |
3.0.1-rc01 | 80 | 11/22/2023 |
3.0.1-alpha01 | 113 | 11/16/2023 |
2.0.2-alpha02 | 128 | 8/30/2023 |
2.0.2-alpha01 | 112 | 8/28/2023 |
2.0.1 | 156 | 8/28/2023 |
2.0.0 | 140 | 8/28/2023 |
1.1.1-alpha | 129 | 8/11/2023 |
1.1.0 | 176 | 8/10/2023 |
1.0.9 | 174 | 7/16/2023 |
1.0.8 | 161 | 7/16/2023 |
1.0.7 | 153 | 6/16/2023 |
1.0.6 | 143 | 6/16/2023 |
1.0.5 | 136 | 6/4/2023 |
1.0.4 | 128 | 6/4/2023 |
1.0.3 | 128 | 6/4/2023 |
1.0.2 | 135 | 6/4/2023 |
1.0.1 | 132 | 6/4/2023 |
1.0.0 | 132 | 6/3/2023 |