【问题标题】:Creating a hardware-specific delegate for an extension method为扩展方法创建特定于硬件的委托
【发布时间】:2019-05-02 23:29:51
【问题描述】:

我正在为原始数值数组编写一个扩展方法库。扩展方法有常规版本和硬件加速版本。我正在尝试使用委托,以便我可以为每个方法设置一个入口点。例如,考虑一个对数组求和的简单扩展方法:

public static class ArrayExtensions
{

  #region Data Members

  private delegate TResult SumArrayDelegate<in TIn, out TResult>( TIn[] array );
  private static SumArrayDelegate<sbyte,int> SumArraySByte_;
  private static SumArrayDelegate<byte,int> SumArrayByte_;
  private static SumArrayDelegate<short,int> SumArrayShort_;
  private static SumArrayDelegate<ushort,int> SumArrayUShort_;
  private static SumArrayDelegate<int,int> SumArrayInt32_;
  private static SumArrayDelegate<uint,uint> SumArrayUInt32_;
  private static SumArrayDelegate<long,long> SumArrayInt64_;
  private static SumArrayDelegate<ulong,ulong> SumArrayUInt64_;
  private static SumArrayDelegate<float,float> SumArrayFloat_;
  private static SumArrayDelegate<double,double> SumArrayDouble_;


  #endregion

  #region Constructor

  static ArrayExtensions()
  {
    if( Avx2.IsSupported )
    {
      SumArraySByte_ = Avx2Utilities.Sum;
      SumArrayByte_ = Avx2Utilities.Sum;
      SumArrayShort_ = Avx2Utilities.Sum;
      SumArrayUShort_ = Avx2Utilities.Sum;
      SumArrayInt32_ = Avx2Utilities.Sum;
      SumArrayUInt32_ = Avx2Utilities.Sum;
      SumArrayInt64_ = Avx2Utilities.Sum;
      SumArrayUInt64_ = Avx2Utilities.Sum;
      SumArrayFloat_ = Avx2Utilities.Sum;
      SumArrayDouble_ = Avx2Utilities.Sum;
    }
    else if( Avx.IsSupported )
    {
      SumArraySByte_ = AvxUtilities.Sum;
      SumArrayByte_ = AvxUtilities.Sum;
      SumArrayShort_ = AvxUtilities.Sum;
      SumArrayUShort_ = AvxUtilities.Sum;
      SumArrayInt32_ = AvxUtilities.Sum;
      SumArrayUInt32_ = AvxUtilities.Sum;
      SumArrayInt64_ = AvxUtilities.Sum;
      SumArrayUInt64_ = AvxUtilities.Sum;
      SumArrayFloat_ = AvxUtilities.Sum;
      SumArrayDouble_ = AvxUtilities.Sum;
    }
    else
    {
      SumArraySByte_ = ArrayUtilities.Sum;
      SumArrayByte_ = ArrayUtilities.Sum;
      SumArrayShort_ = ArrayUtilities.Sum;
      SumArrayUShort_ = ArrayUtilities.Sum;
      SumArrayInt32_ = ArrayUtilities.Sum;
      SumArrayUInt32_ = ArrayUtilities.Sum;
      SumArrayInt64_ = ArrayUtilities.Sum;
      SumArrayUInt64_ = ArrayUtilities.Sum;
      SumArrayFloat_ = ArrayUtilities.Sum;
      SumArrayDouble_ = ArrayUtilities.Sum;
    }
  }

  #endregion

  public static int Sum( this sbyte[] array ) => SumArraySByte_( array );
  public static int Sum( this byte[] array ) => SumArrayByte_( array );
  public static int Sum( this short[] array ) => SumArrayShort_( array );
  public static int Sum( this ushort[] array ) => SumArrayUShort_( array );
  public static int Sum( this int[] array ) => SumArrayInt32_( array );
  public static uint Sum( this uint[] array ) => SumArrayUInt32_( array );
  public static long Sum( this long[] array ) => SumArrayInt64_( array );
  public static ulong Sum( this ulong[] array ) => SumArrayUInt64_( array );
  public static float Sum( this float[] array ) => SumArrayFloat_( array );
  public static double Sum( this double[] array ) => SumArrayDouble_( array );

}

这看起来非常丑陋、多余,并且违反了 DRY 原则。这只是我正在实现的数十种方法中的一种,继续这种当前模式将导致大量的ArrayExtensions 类。

有没有一种不需要明确定义的方法来解决这个问题?或者更确切地说,有没有办法使这个通用,但仅限于支持的类型?扩展方法将仅适用于上述代码中存在的类型。

【问题讨论】:

  • 您似乎重新发明了虚拟方法。有什么理由不为每个架构声明一个抽象类和后代?如果您觉得需要将它们作为静态方法访问,您仍然可以将对象隐藏在静态方法后面。
  • 如何改用ArrayExtensions.Instance.Sum(array),或者使用DI注入实例?
  • 但是,我认为您是在征求人们对什么是更好的方法的意见,这在 Stack Overflow 上是题外话,属于“主要基于意见”。
  • 这个问题很好。让我们不要在过度使用结束规则的情况下扼杀有趣的问题。
  • 那么您将保留静态扩展方法,但将调用转发到 .Instance.Sum(...),这将是此类层次结构。

标签: c# .net delegates


【解决方案1】:

您可以通过使用继承来简化这一点。这将消除其中的大部分。

作为额外的奖励,虚拟呼叫比委托呼叫稍快。使用抽象基类而不是接口来提高性能。

这样做的一个缺点是您在选择要使用的各个方法方面的灵活性较低。

作为替代方案,您可以考虑使用 T4 模板生成这个死记硬背的 C# 代码。它们很好地集成到 IDE 中。

另一种可能性可能是反射。这不会消除所有这些,但会消除一些。

【讨论】:

    【解决方案2】:

    您可以将您的方法声明为:

            public static T Sum<T>(this T[] arry)
            {
                double sum = 0d;
                double dval = 0d;
                foreach (T val in arry)
                {
                    if (double.TryParse(val.ToString(), out dval))
                    {
                        try { sum += dval; }
                        catch (OverflowException) { break; }
                    }
                }
                return (T)Convert.ChangeType(sum, typeof(T));
            }
    
    

    或者手动处理方法中的类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-23
      • 1970-01-01
      相关资源
      最近更新 更多