【问题标题】:Generic Method for Numeric Types数值类型的通用方法
【发布时间】:2013-08-07 12:43:00
【问题描述】:

在 VB.NET 中,如果我想为不同类型的数值变量(IntegerLongDecimalDouble)提供扩展方法,我总是必须为这些定义多种方法:

<Extension()> Public Function Add(a As Integer, b As Integer) As Integer
    Return a + b
End Function

<Extension()> Public Function Add(a As Long, b As Long) As Long
    Return a + b
End Function

<Extension()> Public Function Add(a As Double, b As Double) As Double
    Return a + b
End Function

<Extension()> Public Function Add(a As Decimal, b As Decimal) As Decimal
    Return a + b
End Function

现在对于一个单一的操作,这没问题,但是我想要创建的方法越多,我也需要做更多的重复。

有通用的方法吗?我很想看到这样的东西(伪代码):

<Extension()> _
Public Function Add(Of T As Numeric)(a As T, b As T) As T
    Return a + b
End Function

或者还有其他的概念吗?

【问题讨论】:

  • StevenDoggart 是对的,我的回答在这里不适用,因为:stackoverflow.com/questions/2402660/… 正如评论所说,在您的特定条件下,我将依赖 double (+ 最终转换为 Integer/Decimal ) 让事情变得更简单。

标签: vb.net generics extension-methods numeric


【解决方案1】:

这无法做到,因为您无法将泛型类型限制为一组数字类型(IntegerLongDecimalDouble)。问题是没有IArithmetic 接口可以用来约束T,因此你不能这样写:

' This does not work because IArithmetic does not exist
<Extension()> _
Public Function Add(Of T As IArithmetic)(a As T, b As T) As T
    Return a + b
End Function

但是,您可以通过Microsoft Feedback Center 和提出和/或评论类似请求来说服 Microsoft 实施此计划。

【讨论】:

    【解决方案2】:

    Jon Skeet 用 Generic Operators 中的 Operator 解决了这个问题

    public static class Operator
    {
       public static T And<T>(T value1, T value2)
       {
          return Operator<T>.And(value1, value2);
       }
    }
    
    public static class Operator<T>
    {
       public static Func<T, T, T> And { get { return and; } }
       static Operator()
       {
          add = ExpressionUtil.CreateExpression<T, T, T>(Expression.Add);
       }
    }
    

    @Jeroen Vannevel 对于如何使用 T4 模板生成器方法来解决这个问题有一个很好的答案。 Is there a constraint that restricts my generic method to numeric types?

    【讨论】:

      【解决方案3】:

      虽然这不是您正在寻找的内容,但您可能会发现它在此期间非常有用,可以节省大量打字时间:

      Imports WindowsApplication2.Extensions
      
      Public Module Extensions
          <Extension()>
          Public Function Add(A As Object, B As Object) As Object
              Dim numa, numb As Double
              Dim gooda As Boolean = Double.TryParse(A.ToString, numa)
              Dim goodb As Boolean = Double.TryParse(B.ToString, numb)
              Return numa + numb
          End Function
      End Module
      

      我在那里声明了 2 个布尔值,以防有人需要更强的错误检查。 任何转换为​​字符串且无法解析为双精度的对象都将被视为 0。这样您也可以混合字符串、整数、双精度、长整数、浮点数等。我使用双精度,因为这似乎包含了大多数(如果不是)所有其他号码类型。当然,这很容易改变。

      只需将返回值转换为您需要的任何类型。如果您打开了所有选项,Inellisense 会捕捉到这一点并提示您进行正确的投射

          Dim a As String = "5.0"
          Dim b As Double = CDbl(a.Add(2)) 
      

      我知道这会冒犯一些情感,但就像我说的那样,它可能对某些人有用,作为权宜之计。

      这是一个有趣的article。它是使用 C# 编写的,但您可能会发现其中的概念也很有用。

      【讨论】:

      • 感谢您的建议。不幸的是,这种方式仍然存在两个问题:#1 扩展方法适用于所有类型,#2 始终必须转换为目标时间。
      • 是的,它并不完美,但肯定会节省大量打字时间:) 为避免混淆,您可以在智能感知中添加描述以提醒自己它是哪种 Add 方法。
      【解决方案4】:

      this answer 的启发,以下似乎可行:

      <Extension()> _
      Public Function Add(Of T As {IConvertible, IComparable, IComparable(Of T), 
                                   IFormattable, IEquatable(Of T)}) _
                         (a As T, b As T) As Decimal
          Return a.ToDecimal(Nothing) + b.ToDecimal(Nothing)
      End Function
      

      类型定义将扩展方法限制为接近数字类型。 (但不是 100%,因为您仍然可以在 ByteDate 上调用它,这是不希望的。)

      例子:

      Dim dec As Decimal = Add(CDec(12.34), CDec(34.56))
      Dim sng As Single = CSng(Add(CSng(34.56), CSng(45.67)))
      Dim dbl As Double = CDbl(Add(CDbl(123.34), CDbl(45.123)))
      Dim int As Integer = CInt(Add(CInt(12), CInt(34)))
      Dim lng As Long = CLng(Add(CLng(1200), CLng(3400)))
      

      拥有两个泛型类型还允许使用混合数字类型:

      <Extension()> _
      Public Function Add(Of T As {IConvertible, IComparable, IComparable(Of T), 
                                   IFormattable, IEquatable(Of T)},
                             U As {IConvertible, IComparable, IComparable(Of U), 
                                   IFormattable, IEquatable(Of U)}) _
                         (a As T, b As U) As Decimal
          Return a.ToDecimal(Nothing) + b.ToDecimal(Nothing)
      End Function
      

      例子:

      Dim mixed As Decimal = Add(CDec(12.34), CSng(23.45))
      

      我知道这仍然不理想,因为在转换为十进制或返回时可能会有一些损失。如果您不使用小数,这仍然需要转换函数的结果。但它确实避免了在许多情况下重复的需要。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-30
        • 1970-01-01
        • 1970-01-01
        • 2023-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多