DuckInterface 0.3.0
dotnet add package DuckInterface --version 0.3.0
NuGet\Install-Package DuckInterface -Version 0.3.0
<PackageReference Include="DuckInterface" Version="0.3.0" />
paket add DuckInterface --version 0.3.0
#r "nuget: DuckInterface, 0.3.0"
// Install DuckInterface as a Cake Addin #addin nuget:?package=DuckInterface&version=0.3.0 // Install DuckInterface as a Cake Tool #tool nuget:?package=DuckInterface&version=0.3.0
DuckInterface
This repository contains my attempt to enable duck typing support in C#. It is powered by Roslyn and the new C# 9 feature the Source Generators. I would say it is purely academic/just for fun stuff, but for some scenarios, it can be useful.
How to use it
Let's suppose that you have the next declaration:
public interface ICalculator
{
float Calculate(float a, float b);
}
public class AddCalculator
{
public float Calculate(float a, float b);
}
It is important to notice that the AddCalculator
doesn't implement the ICalculator
in any way. It just has an identical method declaration.
If we try to use it like in the next snippet, we will get a compilation error:
var addCalculator = new AddCalculator();
var result = ApplyCalculator(addCalculator, 10, 20);
float ApplyCalculator(ICalculator calculator, float a, float b)
{
return calculator.Calculate(a, b);
}
In this case, duck typing can be helpful because it will allow us to pass AddCalculator
easily. The DuckInterface
may help with it.
You will need to install the NuGet package and call the Duck<>
extension method where we are passing the variable:
var calculator = new AddCalculator();
var result = ApplyCalculator(calculator.Duck<ICalculator>(), 10, 20);
Console.WriteLine($"Result: {result}");
static float ApplyCalculator(ICalculator calculator, float a, float b)
{
return calculator.Calculate(a, b);
}
And it's done. The compilation errors are gone, and everything works as expected.
How it works
There is a source generator that looks for a method call and variable assignments to understand how the duckable interface may be used. For example, let's look for the next snippet:
var result = ApplyCalculator(addCalculator.Duck<ICalculator>(), 10, 20);
The analyzer will see that the Duck
extension method is utilized, and then it will check the type of addCalculator
variable.
If the type has all the required members, the source generator will generate a container and new extension method with concrete type:
public static class Duck_expDuckable_ICalculator_expDuckable_AddCalculator_Extensions
{
public static TInterface Duck<TInterface>(this global::expDuckable.AddCalculator value)
where TInterface: class
{
return new Duck_expDuckable_ICalculator(value) as TInterface;
}
}
public static class DuckHandlerExtensionsForexpDuckable_ICalculator
{
public static global::expDuckable.ICalculator Create(this IDuckHandler<global::expDuckable.ICalculator> handler, Func<float, float, float> calculate)
{
return new DuckInterface.Generated.expDuckable.Duck_expDuckable_ICalculator(calculate);
}
public static global::expDuckable.ICalculator CreatePartial(this IDuckHandler<global::expDuckable.ICalculator> handler, Func<float, float, float> calculate = default)
{
return new DuckInterface.Generated.expDuckable.Duck_expDuckable_ICalculator(calculate);
}
public static global::expDuckable.ICalculator Make(this IDuckHandler<global::expDuckable.ICalculator> handler, Func<float, float, float> calculate)
{
return new DuckInterface.Generated.expDuckable.Duck_expDuckable_ICalculator(calculate);
}
public static global::expDuckable.ICalculator MakePartial(this IDuckHandler<global::expDuckable.ICalculator> handler, Func<float, float, float> calculate = default)
{
return new DuckInterface.Generated.expDuckable.Duck_expDuckable_ICalculator(calculate);
}
}
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
public partial class Duck_expDuckable_ICalculator: global::expDuckable.ICalculator
{
public Duck_expDuckable_ICalculator(Func<float, float, float> calculate)
{
_Calculate = calculate;
}
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private readonly Func<float, float, float> _Calculate;
[System.Diagnostics.DebuggerStepThrough]
public float Calculate(float a, float b)
{
return _Calculate(a, b);
}
}
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. |
.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. |
This package has 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.