Typage de canard et C #

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!







Divulgacher

. . , .







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



. , , .

. !







Nuget ici

Github ici








All Articles