Tenekon.MethodOverloads.SourceGenerator by Tenekon
NuGet / site data
Details
Info
Name: Tenekon.MethodOverloads.SourceGenerator
C# source generator that creates extension method overloads by treating a parameter window as optional and emitting legal, unique subsequences.
Author: Tenekon
NuGet: https://www.nuget.org/packages/Tenekon.MethodOverloads.SourceGenerator/
You can find more details at https://github.com/tenekon/Tenekon.MethodOverloads.SourceGenerator
Source: https://github.com/tenekon/Tenekon.MethodOverloads.SourceGenerator
Author
Tenekon

Original Readme
Tenekon.MethodOverloads.SourceGenerator
A C# source generator that creates extension overloads by treating a selected parameter window as optional and emitting legal, unique subsequences. It supports matchers, bucketized output, visibility overrides, subsequence strategies, and generic substitution via SupplyParameterType.
Install
<ItemGroup>
<PackageReference Include="Tenekon.MethodOverloads.SourceGenerator" Version="x.y.z" PrivateAssets="all" />
</ItemGroup>
Quickstart
- Add
[GenerateOverloads]to a method, or[GenerateMethodOverloads(Matchers = ...)]to a type. - (Optional) Add
[OverloadGenerationOptions(...)]to control matching and output. - Build. Generated code appears as
MethodOverloads_<Namespace>*.g.cs.
Core Concepts
- Window: the parameter range that can be omitted to produce overloads.
- ExcludeAny: a list of parameter names that must be omitted in every overload within the window.
- Matchers: define windows on matcher methods and apply them to target methods.
- Bucketization: route generated methods into a specific static partial class.
- SupplyParameterType: substitute method type parameters with concrete types before generation.
Examples
######### 1) Basic Window
Input:
namespace Demo;
using Tenekon.MethodOverloads;
public sealed class OrderService
{
[GenerateOverloads(Begin = nameof(tenantId))]
public void CreateOrder(string orderId, string tenantId, bool requireApproval) \{ }
}
Output:
namespace Demo;
public static class MethodOverloads
{
public static void CreateOrder(this OrderService source, string orderId) =>
source.CreateOrder(orderId, tenantId: default(string), requireApproval: default(bool));
public static void CreateOrder(this OrderService source, string orderId, string tenantId) =>
source.CreateOrder(orderId, tenantId, requireApproval: default(bool));
public static void CreateOrder(this OrderService source, string orderId, bool requireApproval) =>
source.CreateOrder(orderId, tenantId: default(string), requireApproval);
}
######### 2) Window Variants
Use Begin, End, BeginExclusive, EndExclusive, or the constructor GenerateOverloads(string beginEnd).
[GenerateOverloads(BeginExclusive = nameof(start), End = nameof(end))]
public void Query(int start, int end, bool includeMetadata, string? tag) \{ }
######### 3) ExcludeAny (Forced Omissions)
ExcludeAny forces specific parameters inside the window to be omitted in every generated overload.
[GenerateOverloads(Begin = nameof(optionalA), End = nameof(optionalC), ExcludeAny = [nameof(optionalB)])]
public void Configure(int required, string? optionalA, string? optionalB, string? optionalC) \{ }
Notes:
ExcludeAnycannot be combined withMatcherson the same attribute.- If ExcludeAny covers the whole window, no overloads are generated for that attribute.
######### 4) Matcher-Based Generation (Method-Level)
Input:
namespace Demo;
using Tenekon.MethodOverloads;
public sealed class UserService
{
[GenerateOverloads(Matchers = [typeof(UserMatchers)])]
public void UpdateUser(string id, string name, int level, bool active) \{ }
}
internal interface UserMatchers
{
[GenerateOverloads(nameof(paramB))]
void UpdateUser(int paramA, bool paramB);
}
Output:
namespace Demo;
public static class MethodOverloads
{
public static void UpdateUser(this UserService source, string id, string name, int level) =>
source.UpdateUser(id, name, level, active: default(bool));
}
######### 5) Matcher-Based Generation (Type-Level + Static Target)
Input:
namespace Demo;
using Tenekon.MethodOverloads;
[GenerateMethodOverloads(Matchers = [typeof(MathMatchers)])]
public static class MathUtils
{
public static void Multiply(int left, int right, bool checkedOverflow) \{ }
}
internal interface MathMatchers
{
[GenerateOverloads(nameof(paramB))]
void Multiply(int paramA, bool paramB);
}
Output:
namespace Demo;
public static class MethodOverloads
{
extension(MathUtils)
{
public static void Multiply(int left, int right) =>
MathUtils.Multiply(left, right, checkedOverflow: default(bool));
}
}
######### 6) Range Anchor Match Mode
RangeAnchorMatchMode.TypeOnly (default) matches by type only.
RangeAnchorMatchMode.TypeAndName requires matching names as well.
[OverloadGenerationOptions(RangeAnchorMatchMode = RangeAnchorMatchMode.TypeAndName)]
[GenerateOverloads(Matchers = [typeof(ServiceMatchers)])]
public void Call(string id, string name, bool active) \{ }
######### 7) Subsequence Strategy
OverloadSubsequenceStrategy.UniqueBySignature (default) generates all unique overloads.
OverloadSubsequenceStrategy.PrefixOnly generates only prefix omissions.
[OverloadGenerationOptions(SubsequenceStrategy = OverloadSubsequenceStrategy.PrefixOnly)]
[GenerateOverloads(Begin = nameof(optionalA))]
public void Configure(int required, string? optionalA, bool optionalB) \{ }
######### 8) Overload Visibility
[OverloadGenerationOptions(OverloadVisibility = OverloadVisibility.Internal)]
[GenerateOverloads(Begin = nameof(optionalA))]
public void Configure(int required, string? optionalA, bool optionalB) \{ }
######### 9) Bucketization (Scoped Static Classes)
Route generated overloads into a specific static partial class:
public static partial class MyBucket
{
}
[OverloadGenerationOptions(BucketType = typeof(MyBucket))]
[GenerateOverloads(Begin = nameof(optionalA))]
public void Configure(int required, string? optionalA, bool optionalB) \{ }
Output:
public static partial class MyBucket
{
public static void Configure(this /* target type */ source, int required) =>
source.Configure(required, optionalA: default(string), optionalB: default(bool));
}
######### 10) SupplyParameterType (Generic Substitution)
Replace method type parameters with concrete types in generated overloads and invocations.
public sealed class Constraint \{ }
public interface IService<T> \{ }
public sealed class Api
{
[GenerateOverloads(nameof(optionalObject))]
[SupplyParameterType(nameof(TConstraint), typeof(Constraint))]
public void Use<TConstraint>(IService<TConstraint>? service, object? optionalObject) \{ }
}
Output:
public static class MethodOverloads
{
public static void Use(this Api source, IService<Constraint>? service) =>
source.Use<Constraint>(service, default(object?));
}
If only some method type parameters are supplied, the overload stays generic for the remaining ones.
######### 11) Generic Containing Types
Containing type type parameters and constraints are preserved on generated overloads.
public sealed class Container<T> where T : class, new()
{
[GenerateOverloads(nameof(optionalObject))]
public void Create(T value, object? optionalObject) \{ }
}
Generated overloads keep T and its constraints.
MSBuild Options
Emit attributes only (skip generation and diagnostics):
<PropertyGroup>
<TenekonMethodOverloadsSourceGeneratorAttributesOnly>true</TenekonMethodOverloadsSourceGeneratorAttributesOnly>
</PropertyGroup>
Diagnostics
Diagnostics are reported by the analyzer and surfaced during build:
MOG001Invalid window anchor.MOG002Matcher has no subsequence match.MOG003Defaults inside window.MOG004Params outside window.MOG005Ref/out/in omitted.MOG006Duplicate signature skipped.MOG007Conflicting window anchors (BeginEnd vs Begin/End).MOG008Redundant Begin and End.MOG009Begin and BeginExclusive conflict.MOG010End and EndExclusive conflict.MOG011Parameterless target method.MOG012Matchers + window anchors conflict.MOG013Invalid bucket type.MOG014Invalid SupplyParameterType usage.MOG015SupplyParameterType refers to missing type parameter.MOG016Conflicting SupplyParameterType mappings.MOG017Matchers + ExcludeAny conflict.MOG018ExcludeAny refers to missing/out-of-window parameter.MOG019ExcludeAny contains invalid entries.
You can downgrade error-level diagnostics in .globalconfig if you need the project to compile with intentional violations.
Generation Rules (Summary)
- Only ordinary, non-private methods are eligible.
- Window omissions cannot drop
ref/out/inparameters. paramsmust be inside the optional window to be omitted.- Existing method signatures are not duplicated.
- Defaults inside the window are not allowed.
- Matcher usages are emitted only when the matcher type is at least
internal.
Docs
See docs/generator.md for detailed behavior and docs/acceptance-criterias.md for the acceptance project structure.
About
generate overloads of the same method
How to use
Example (source csproj, source files)
- CSharp Project
- Person.cs
- Program.cs
This is the CSharp Project that references Tenekon.MethodOverloads.SourceGenerator
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Tenekon.MethodOverloads.SourceGenerator" Version="0.0.6">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
<CompilerGeneratedFilesOutputPath>$(BaseIntermediateOutputPath)\GX</CompilerGeneratedFilesOutputPath>
</PropertyGroup>
</Project>
This is the use of Tenekon.MethodOverloads.SourceGenerator in Person.cs
namespace overloadMethod;
public class Person
{
public string FirstName \{ get; set; \} = string.Empty;
public string LastName \{ get; set; \} = string.Empty;
[Tenekon.MethodOverloads.GenerateOverloads(Begin = nameof(MiddleName))]
public string FullName(string MiddleName, bool ToLowerCase)
{
var fullName = $"{FirstName} {MiddleName} {LastName}";
return ToLowerCase ? fullName.ToLower() : fullName;
}
}
This is the use of Tenekon.MethodOverloads.SourceGenerator in Program.cs
using overloadMethod;
Person p = new();
p.FirstName= "Andrei";
p.LastName = "Ignat";
Console.WriteLine(p.FullName());
Generated Files
Those are taken from $(BaseIntermediateOutputPath)\GX
- EmbeddedAttribute.g.cs
- GenerateMethodOverloadsAttribute.g.cs
- GenerateOverloadsAttribute.g.cs
- MatcherUsageAttribute.g.cs
- MethodOverloads_overloadMethod.g.cs
- OverloadGenerationOptionsAttribute.g.cs
- SupplyParameterTypeAttribute.g.cs
namespace Microsoft.CodeAnalysis;
internal sealed partial class EmbeddedAttribute : global::System.Attribute;
#nullable enable
namespace Tenekon.MethodOverloads;
[global::Microsoft.CodeAnalysis.Embedded]
[global::System.AttributeUsage(
global::System.AttributeTargets.Class
| global::System.AttributeTargets.Struct
| global::System.AttributeTargets.Interface,
AllowMultiple = true)]
internal sealed class GenerateMethodOverloadsAttribute : global::System.Attribute
{
public global::System.Type[]? Matchers \{ get; set; }
}
#nullable enable
namespace Tenekon.MethodOverloads;
[global::Microsoft.CodeAnalysis.Embedded]
[global::System.AttributeUsage(global::System.AttributeTargets.Method, AllowMultiple = true)]
internal sealed class GenerateOverloadsAttribute : global::System.Attribute
{
public GenerateOverloadsAttribute()
{
}
public GenerateOverloadsAttribute(string beginEnd)
{
Begin = beginEnd;
End = beginEnd;
}
/// <summary>
/// All parameters beginning from <see cref="Begin"/> (inclusive) are considered for optional or required.
/// </summary>
public string? Begin \{ get; set; }
/// <summary>
/// All parameters after <see cref="BeginExclusive"/> are considered for optional or required.
/// </summary>
public string? BeginExclusive \{ get; set; }
/// <summary>
/// All parameters before <see cref="EndExclusive"/> are considered for optional or required.
/// </summary>
public string? EndExclusive \{ get; set; }
/// <summary>
/// All parameters until <see cref="End"/> (inclusive) are considered for optional or required.
/// </summary>
public string? End \{ get; set; }
/// <summary>
/// Parameters listed here are always omitted from generated overloads within the resolved window.
/// </summary>
public string[]? ExcludeAny \{ get; set; }
public global::System.Type[]? Matchers \{ get; set; }
}
#nullable enable
namespace Tenekon.MethodOverloads;
[global::Microsoft.CodeAnalysis.Embedded]
[global::System.AttributeUsage(global::System.AttributeTargets.Class, AllowMultiple = true)]
internal sealed class MatcherUsageAttribute : global::System.Attribute
{
public MatcherUsageAttribute(string methodName)
{
}
}
// <auto-generated/>
#nullable enable
namespace overloadMethod;
public static class MethodOverloads
{
public static string FullName(this global::overloadMethod.Person source, bool ToLowerCase) => source.FullName(default(string), ToLowerCase);
public static string FullName(this global::overloadMethod.Person source, string MiddleName) => source.FullName(MiddleName, default(bool));
public static string FullName(this global::overloadMethod.Person source) => source.FullName(default(string), default(bool));
}
#nullable enable
namespace Tenekon.MethodOverloads;
[global::Microsoft.CodeAnalysis.Embedded]
internal enum RangeAnchorMatchMode
{
TypeOnly,
TypeAndName
}
[global::Microsoft.CodeAnalysis.Embedded]
internal enum OverloadSubsequenceStrategy
{
PrefixOnly,
UniqueBySignature
}
[global::Microsoft.CodeAnalysis.Embedded]
internal enum OverloadVisibility
{
MatchTarget,
Public,
Internal,
Private
}
[global::Microsoft.CodeAnalysis.Embedded]
[global::System.AttributeUsage(
global::System.AttributeTargets.Class | global::System.AttributeTargets.Struct
| global::System.AttributeTargets.Interface | global::System.AttributeTargets.Method)]
internal sealed class OverloadGenerationOptionsAttribute : global::System.Attribute
{
public RangeAnchorMatchMode RangeAnchorMatchMode \{ get; set; }
public OverloadSubsequenceStrategy SubsequenceStrategy \{ get; set; }
public OverloadVisibility OverloadVisibility \{ get; set; }
public global::System.Type? BucketType \{ get; set; }
}
#nullable enable
namespace Tenekon.MethodOverloads;
[global::Microsoft.CodeAnalysis.Embedded]
[global::System.AttributeUsage(
global::System.AttributeTargets.Method
| global::System.AttributeTargets.Class
| global::System.AttributeTargets.Interface,
AllowMultiple = true)]
internal sealed class SupplyParameterTypeAttribute : global::System.Attribute
{
public SupplyParameterTypeAttribute(string typeParameterName, global::System.Type suppliedType)
{
TypeParameterName = typeParameterName;
SuppliedType = suppliedType;
}
public string TypeParameterName \{ get; }
public global::System.Type SuppliedType \{ get; }
public object? Group \{ get; set; }
}
Useful
Download Example (.NET C#)
Share Tenekon.MethodOverloads.SourceGenerator
https://ignatandrei.github.io/RSCG_Examples/v2/docs/Tenekon.MethodOverloads.SourceGenerator
Category "EnhancementClass" has the following generators:
1 AOP.Logging.SourceGenerator
2026-08-30
2 ApparatusAOT
2023-04-16
3 AspectGenerator
2024-01-07
4 CommonCodeGenerator
2024-04-03
5 Comparison
2025-05-25
6 DudNet
2023-10-27
7 Enhanced.GetTypes
2024-09-17
8 FastGenericNew
2023-08-10
9 Immutype
2023-08-12
10 Ling.Audit
2023-12-12
11 Lombok.NET
2023-04-16
12 M31.FluentAPI
2023-08-25
13 MemberAccessor
2025-03-24
14 MemoryPack
2023-08-04
15 Meziantou.Polyfill
2023-10-10
16 Microsoft.Extensions.Logging
2023-04-16
17 Microsoft.Extensions.Options.Generators.OptionsValidatorGenerator
2023-11-17
18 Microsoft.Interop.JavaScript.JSImportGenerator 2023-04-16
19 NLog.Extensions.ThisClass
2026-04-03
20 OptionToStringGenerator
2024-02-15
21 Pekspro.DataAnnotationValuesExtractor
2026-02-15
22 Program
2025-11-06
23 QueryStringGenerator
2024-11-07
24 RSCG_Decorator
2023-09-30
25 RSCG_UtilityTypes
2023-12-22
26 StaticReflection
2023-10-13
27 SyncMethodGenerator
2023-08-14
28 System.Runtime.InteropServices
2023-04-16
29 System.Text.RegularExpressions
2023-04-16
30 TelemetryLogging
2023-11-30
31 Tenekon.MethodOverloads.SourceGenerator
2026-09-05
32 ThisClass
2024-04-19
33 TrimItEasy
2026-08-28