Buen día. Últimamente he estado experimentando mucho con .Net 5 y sus generadores de fuentes. Y de repente tuve una idea de cómo puedo usar los generadores de código fuente para implementar la "escritura pato" en C #. No podía dejar atrás esta idea. Como resultado, diría, salió algo puramente akámico (nadie usará esto en producción, espero), pero el resultado es bastante interesante. ¡Cualquiera interesado en pedir un corte!
No profundizaré en la implementación en sí. Se puede ver en el repositorio, cuyo enlace estará debajo. No hay nada difícil para aquellos que ya han incursionado en los generadores, pero para todos los demás, se requiere un artículo mucho más extenso.
Cómo usarlo
Imaginemos que tenemos el siguiente ejemplo:
public interface ICalculator
{
float Calculate(float a, float b);
}
public class AddCalculator
{
float Calculate(float a, float b);
}
Es importante señalar que AddCalculator
no se implementa de ninguna manera ICalculator
.
Solo tienen firmas idénticas. Si intentamos usarlos de la siguiente manera, fallaremos:
var addCalculator = new AddCalculator();
var result = Do(addCalculator, 10, 20);
float Do(ICalculator calculator, float a, float b)
{
return calculator.Calculate(a, b);
}
El compilador de C # dirá lo siguiente:
Argument type 'AddCalculator' is not assignable to parameter type 'ICalculator'
Y tendrá razón. Pero dado que la firma es AddCalculator
completamente la misma ICalculator
y realmente queremos hacer esto, la solución podría ser la escritura pato, que no funciona en C #. Aquí es donde el paquete nuget resulta útil DuckInterface
. Todo lo que necesita hacer es instalarlo y modificar un poco nuestras firmas. Comencemos con la interfaz agregándole un atributo 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
. , , .
. !