Reputation: 2157
In VB.NET if I want to have an extension method for numerical variables of different types (Integer
, Long
, Decimal
, Double
) I always have to define multiple methods for these:
<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
Now for one single operation this is alright, but the more methods I want to create the more duplicates I have do to, too.
Is there a generic way to do so? I would love to see something like this (pseudo-code):
<Extension()> _
Public Function Add(Of T As Numeric)(a As T, b As T) As T
Return a + b
End Function
Or is there any other concept for doing so?
Upvotes: 3
Views: 2248
Reputation: 6668
Jon Skeet solved this with Operator<T> in 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 has a great answer on how to solve this using a T4 template to generator methods. Is there a constraint that restricts my generic method to numeric types?
Upvotes: 1
Reputation: 2157
Inspired by this answer the following seems to work:
<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
The type definition restricts the extension method close to numeric types. (Not 100%, though, as you could still call this on Byte
and Date
which isn't desired.)
Examples:
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)))
Having two generic types also allows using mixed numeric types:
<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
Example:
Dim mixed As Decimal = Add(CDec(12.34), CSng(23.45))
I know that this is still not ideal because there might be some loss when converting to decimal or back. Also this still requires casting the result of the function if you're not working with decimals. But it does avoid the need of duplicates in many cases.
Upvotes: 0
Reputation: 6948
While this isn't exactly what you're looking for, you might find it useful in the interim to save a lot of typing:
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
I declared 2 booleans in there in case anyone desires stronger error checking. Any object cast to string that can't be parsed to a double will be treated as 0. This way as well you can mix string, int, double, long, float, etc. I used double since this seems to encompass most if not all the other number types. Of course this can easily be changed.
simply cast the return to whichever type you need. Inellisense will catch this and prompt you for the right casting, if you have all your options turned on
Dim a As String = "5.0"
Dim b As Double = CDbl(a.Add(2))
I know that this will offend some sensibilities, but like I said it might be useful to some people, as a stopgap measure.
Here's an interesting article. It's written using C#, but you might find the concepts useful as well.
Upvotes: 0
Reputation: 34846
This cannot be done, because you cannot constrain a generic type to a group of numeric types (Integer
, Long
, Decimal
, Double
). The problem is that there is no IArithmetic
interface that you could you use to constrain T
to, therefore you cannot write this:
' 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
However, you can join the cause to convince Microsoft to implement this by the Microsoft Feedback Center and proposing and/or commenting on similar requests.
Upvotes: 2