良い一日。私は最近、.Net5とそのソースジェネレーターで多くの実験を行っています。そして、私は突然、ソースジェネレータを使用してC#で「ダックタイピング」を実装する方法を思いつきました。この考えをそのままにしておくことはできませんでした。その結果、純粋にアカミックなものが出てきたと思いますが(これを本番環境で使用する人はいないと思います)、結果は非常に興味深いものです。カットを求めることに興味がある人は誰でも!
実装自体については詳しく説明しません。リポジトリで表示できます。リンクは以下にあります。すでにジェネレーターに手を出している人にとっては難しいことではありませんが、他のすべての人にとっては、はるかに大きな記事が必要です。
それの使い方
次の例があると想像してみましょう。
public interface ICalculator
{
float Calculate(float a, float b);
}
public class AddCalculator
{
float Calculate(float a, float b);
}
AddCalculator
いかなる方法でも実装されないことに注意することが重要ICalculator
です。
それらは同一の署名しか持っていません。次のように使用しようとすると、失敗します。
var addCalculator = new AddCalculator();
var result = Do(addCalculator, 10, 20);
float Do(ICalculator calculator, float a, float b)
{
return calculator.Calculate(a, b);
}
C#コンパイラは次のように言います。
Argument type 'AddCalculator' is not assignable to parameter type 'ICalculator'
そして彼は正しいでしょう。しかし、署名はAddCalculator
完全に同じでICalculator
あり、本当にこれを実行したいので、解決策はC#では機能しないダックタイピングである可能性があります。これは、nugetパッケージが役立つところDuckInterface
です。あなたがする必要があるのはそれをインストールし、私たちの署名を少し微調整することです。インターフェイスに属性を追加して、インターフェイスから始めましょうDuckable
。
[Duckable]
public interface ICalculator
{
float Calculate(float a, float b);
}
Do
. ICalculator
DICalculator
. DICalculator
DuckInterface
.
DICalculator
ICalculator
. IDE. DICalculator
.
:
var addCalculator = new AddCalculator();
var result = Do(addCalculator, 10, 20);
float Do(DICalculator calculator, float a, float b)
{
return calculator.Calculate(a, b);
}
. .
. Duckable
"" . , ICalculator
:
public partial class DICalculator : ICalculator
{
[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);
}
}
duckable . :
var result = Do(addCalculator, 10, 20);
Do
DICalculator
, addCalculator
. , DICalculator
:
public partial class DICalculator
{
private DICalculator(global::AddCalculator value)
{
_Calculate = value.Calculate;
}
public static implicit operator DICalculator(global::AddCalculator value)
{
return new DICalculator(value);
}
}
DICalculator
partial class . , :
:
[Duckable]
public interface ICalculator
{
float Zero { get; }
float Value { get; set; }
float Calculate(float a, float b);
}
// ....
public partial class DICalculator : ICalculator
{
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private readonly Func<float> _ZeroGetter;
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private readonly Func<float> _ValueGetter;
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private readonly Action<float> _ValueSetter;
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
private readonly Func<float, float, float> _Calculate;
public float Zero
{
[System.Diagnostics.DebuggerStepThrough] get { return _ZeroGetter(); }
}
public float Value
{
[System.Diagnostics.DebuggerStepThrough] get { return _ValueGetter(); }
[System.Diagnostics.DebuggerStepThrough] set { _ValueSetter(value); }
}
[System.Diagnostics.DebuggerStepThrough]
public float Calculate(float a, float b)
{
return _Calculate(a, b);
}
}
. - duck typing . . ref struct-. , . , where - :
float Do<TCalcualtor>(TCalcualtor calculator, float a, float b)
where TCalcualtor: DICalculator
{
return calculator.Calculate(a, b);
}
zero cost duct typing( , ), , partial class
partial struct
duck . , Do
TCalcualtor
. , , .
. !