Digitação Duck e C #

Dia bom. Recentemente, experimentei muito com .Net 5 e seus geradores de fonte. E de repente tive a ideia de como posso usar os Geradores de código-fonte para implementar "digitação de pato" em C #. Eu não poderia simplesmente deixar essa ideia para trás. Como resultado, eu diria, algo puramente acâmico saiu (ninguém vai usar isso na produção, espero), mas o resultado é bastante interessante. Quem estiver interessado em pedir um corte!







Spoiler

Não vou me aprofundar na implementação em si. Ele pode ser visualizado no repositório, cujo link estará abaixo. Não há nada difícil para aqueles que já se envolveram com geradores, mas para todos os outros, um artigo muito maior é necessário.







Como usá-lo



Vamos imaginar que temos o seguinte exemplo:







public interface ICalculator
{
  float Calculate(float a, float b);
}

public class AddCalculator
{

  float Calculate(float a, float b);
}
      
      





É importante notar que AddCalculator



ele não implementa de forma alguma ICalculator



.

Eles só têm assinaturas idênticas. Se tentarmos usá-los da seguinte maneira, falharemos:







var addCalculator = new AddCalculator();

var result = Do(addCalculator, 10, 20);

float Do(ICalculator calculator, float a, float b)
{
  return calculator.Calculate(a, b);
}

      
      





O compilador C # dirá o seguinte:







Argument type 'AddCalculator' is not assignable to parameter type 'ICalculator'









E ele vai estar certo. Mas como a assinatura é AddCalculator



completamente a mesma ICalculator



e realmente queremos fazer isso, a solução pode ser a digitação de pato, que não funciona em C #. É aqui que o pacote nuget é útil DuckInterface



. Tudo o que você precisa fazer é instalá-lo e ajustar um pouco nossas assinaturas. Vamos começar com a interface adicionando um atributo a ela 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



. , , .

. !







Nuget aqui

Github aqui








All Articles