数值类型的通用方法

时间:2013-08-07 12:43:01

标签: vb.net generics extension-methods numeric

在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

或者还有其他概念吗?

4 个答案:

答案 0 :(得分:2)

无法执行此操作,因为您无法将通用类型约束为一组数字类型(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来实现这一点,并提出和/或评论类似的请求。

答案 1 :(得分:1)

Jon Skeet用Operator&lt; T&gt;解决了这个问题。在Generic Operators

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?

答案 2 :(得分:0)

虽然这不是您正在寻找的内容,但您可能会发现在此期间保存大量输入非常有用:

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个布尔值,以防任何人想要更强的错误检查。 任何转换为​​无法解析为double的字符串的对象都将被视为0.这样你也可以混合使用string,int,double,long,float等。我使用了double,因为这似乎包含了大部分内容,如果不是所有其他数字类型。当然,这很容易改变。

只需将返回值转换为您需要的任何类型。如果你打开了所有选项,Inellisense会抓住这个并提示你正确的施法

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

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

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

答案 3 :(得分:0)

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))

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