Schönen Tag. Vor kurzem habe ich viel mit .Net 5 und seinen Quellgeneratoren experimentiert. Und plötzlich bekam ich eine Idee, wie ich mit Quellgeneratoren "Enten-Typisierung" in C # implementieren kann. Ich konnte diese Idee nicht einfach hinter mir lassen. Am Ende, würde ich sagen, kam eine rein akamische Sache heraus (ich hoffe, niemand wird diese zum Verkauf anbieten), aber das Ergebnis ist ziemlich interessant. Jeder, der nach einem Schnitt fragen möchte!
. . , .
Wie man es benutzt
Stellen wir uns vor, wir haben das folgende Beispiel:
public interface ICalculator
{
float Calculate(float a, float b);
}
public class AddCalculator
{
float Calculate(float a, float b);
}
Es ist wichtig zu beachten, dass AddCalculator
es in keiner Weise implementiert wird ICalculator
.
Sie haben nur identische Signaturen. Wenn wir versuchen, sie wie folgt zu verwenden, schlagen wir fehl:
var addCalculator = new AddCalculator();
var result = Do(addCalculator, 10, 20);
float Do(ICalculator calculator, float a, float b)
{
return calculator.Calculate(a, b);
}
Der C # -Compiler sagt Folgendes:
Argument type 'AddCalculator' is not assignable to parameter type 'ICalculator'
Und er wird recht haben. Da die Signatur jedoch AddCalculator
völlig identisch ist ICalculator
und wir dies wirklich tun möchten, besteht die Lösung möglicherweise in der Eingabe von Enten, die in C # nicht funktioniert. Hier bietet sich das Nuget-Paket an DuckInterface
. Alles was Sie tun müssen, ist es zu installieren und unsere Signaturen ein wenig zu optimieren. Beginnen wir mit der Schnittstelle, indem wir ihr ein Attribut hinzufügen 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
. , , .
. !