Write a method once. Get one per format, type or mode.
A source generator that replaces T4 templates. Write SaveAsBmp by hand, list the other formats, and SaveAsGif, SaveAsPng and the rest are generated on every build, in any IDE.
[CloneVariants("Bmp", "Gif", "Png")]
public static partial class ImageExtensions
{
/// <summary>Saves the image in the Bmp format.</summary>
public static void SaveAsBmp(this Image image, Stream stream, BmpEncoder? encoder)
=> image.Save(stream, encoder ?? BmpFormat.Instance.DefaultEncoder);
}/// <summary>Saves the image in the Gif format.</summary>
public static void SaveAsGif(this Image image, Stream stream, GifEncoder? encoder)
=> image.Save(stream, encoder ?? GifFormat.Instance.DefaultEncoder);
/// <summary>Saves the image in the Png format.</summary>
public static void SaveAsPng(this Image image, Stream stream, PngEncoder? encoder)
=> image.Save(stream, encoder ?? PngFormat.Instance.DefaultEncoder);Simplified for reading: the generated code fully qualifies every name.
<PackageReference Include="Zomp.MethodCloning.Variants" PrivateAssets="all" />Works with the .NET 8 SDK or later. Nothing is added to your assembly.
| Where | Bmp becomes Gif |
|---|---|
| The method's name | SaveAsBmp to SaveAsGif (the name must contain the word) |
| Types it uses | Formats.Bmp.BmpEncoder to Formats.Gif.GifEncoder |
| Static calls by simple name | SaveAsBmp(image, stream) to SaveAsGif(image, stream) |
| Its documentation | "in the Bmp format" to "in the Gif format" |
| The file's using directives | using Formats.Bmp; to using Formats.Gif; |
A type with no counterpart, such as a BmpQuantizer with no GifQuantizer, is kept, with warning ZMC003.
- On a type, the attribute covers every method in that declaration. On a method, it covers just that method, and wins over the type's.
- Nullability follows your method. Write it in
#nullable disableto match what a T4 template produced. - The word is replaced everywhere. With
Normalas the word,Normalize()would becomeMultiplyize(). Pick a word that appears nowhere else. - Not swapped: generic and nested types, calls written as
image.SaveAsBmp(), string literals, and comments inside the method. - Not cloned: members of C# 14 extension blocks (ZMC004).
Full rules, limitations and a step-by-step migration from T4: the reference.
| ID | Severity | Means | Fix |
|---|---|---|---|
| ZMC001 | Error | Two variants would declare the same member | Remove the duplicate from the list |
| ZMC002 | Error | The method's name lacks the word being swapped | Rename the method, or take the attribute off it |
| ZMC003 | Warning | A type has no counterpart, so it is kept | Add the counterpart, or suppress if intended |
| ZMC004 | Error | The method is in a C# 14 extension block | Write it as a classic extension method |