Bonne journée. Récemment, j'ai beaucoup expérimenté avec .Net 5 et ses générateurs de sources. Et j'ai soudainement eu une idée de la façon dont je peux utiliser les générateurs de source pour implémenter le "typage canard" en C #. Je ne pouvais pas laisser cette idée derrière moi. En conséquence, je dirais, une chose purement akamique est sortie (personne ne l'utilisera en production, j'espère), mais le résultat est assez intéressant. Toute personne intéressée à demander une coupe!
. . , .
Comment l'utiliser
Imaginons que nous ayons l'exemple suivant:
public interface ICalculator
{
float Calculate(float a, float b);
}
public class AddCalculator
{
float Calculate(float a, float b);
}
Il est important de noter AddCalculator
qu'il ne met en œuvre en aucune façon ICalculator
.
Ils n'ont que des signatures identiques. Si nous essayons de les utiliser de la manière suivante, nous échouerons:
var addCalculator = new AddCalculator();
var result = Do(addCalculator, 10, 20);
float Do(ICalculator calculator, float a, float b)
{
return calculator.Calculate(a, b);
}
Le compilateur C # dira ce qui suit:
Argument type 'AddCalculator' is not assignable to parameter type 'ICalculator'
Et il aura raison. Mais comme la signature est AddCalculator
complètement la même ICalculator
et que nous voulons vraiment faire cela, la solution pourrait être de taper duck qui ne fonctionne pas en C #. C'est là que le paquet nuget est utile DuckInterface
. Tout ce que vous avez à faire est de l'installer et d'ajuster un peu nos signatures. Commençons par l'interface en y ajoutant un attribut 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
. , , .
. !