Dan Tao
Dan Tao

Reputation: 128317

Is it possible to write a generic function in .NET that only accepts numerical types?

Suppose I want to write a function like the following (as usual, a trivial example for illustrative purposes):

Public Function calcSqSum(Of T)(ByVal list As IEnumerable(Of T)) As T
    Dim sumSq As T

    For Each item As T In list
        sumSq += (item * item)
    Next

    Return sumSq
End Function

As you can probably guess, this function causes an error because a generic object is not guaranteed to implement the + operator. As far as I know, though, any numerical type (Integer, Double, Decimal, etc.) will.

Is there a way to write a (quasi-)generic function that can accept any numerical type, without having to explicitly overload the function for every such type yourself?

Alternatively, I suppose an equally acceptable solution would be to somehow check if a type implements the '+' operator (or any operator generally associated with numerical types and used by the function).

Upvotes: 3

Views: 1157

Answers (5)

SommerEngineering
SommerEngineering

Reputation: 1700

C# 10 and .NET 6: Solution from November 2021 onwards

Great news: there is now a solution for this in .NET 6 and C# 10, cf. https://devblogs.microsoft.com/dotnet/preview-features-in-net-6-generic-math/#generic-math

I cannot estimate at the moment if this function will be available in VB.NET as well.

In order to accept any numerical type, use the INumber<T> interface, e.g.:

public T Adding<T>(T a, T b)
   where T : INumber<T>
   where T : IAdditionOperators<T, T, T>
{
   return a + b;
}

Note: At the time of my answer, this feature was only a preview. Microsoft will keep this for the final version of .NET 6 as they still want to allow breaking chances. To use the feature, preview features must be enabled in the project configuration:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <EnablePreviewFeatures>true</EnablePreviewFeatures>
    <LangVersion>preview</LangVersion>
    <OutputType>Exe</OutputType>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="System.Runtime.Experimental" Version="6.0.0-preview.7.21377.19" />
  </ItemGroup>

</Project>

Upvotes: 0

Derek Tomes
Derek Tomes

Reputation: 4007

You could use overloading instead. Either write an essentially identical function of each numeric type you want to support:

Public Function calcSqSum(ByVal list As IEnumerable(Of Integer)) As Integer
    Dim sumSq As Integer
    For Each item As Integer In list
        sumSq += (item * item)
    Next
    Return sumSq
End Function

Public Function calcSqSum(ByVal list As IEnumerable(Of Double)) As Double
    Dim sumSq As Double
    For Each item As Double In list
        sumSq += (item * item)
    Next
    Return sumSq
End Function

etc

Or, if it has a lot of code in it, make your generic function private and wrap it with overload public functions:

Private Function calcSqSum1(Of T)(ByVal list As IEnumerable(Of T)) As T
    Dim sumSq As T

    For Each item As T In list
        sumSq += (item * item)
    Next

    Return sumSq
End Function

Public Function calcSqSum(ByVal list As IEnumerable(Of Integer)) As Integer
    Return calcSqSum1(list)
End Function

Public Function calcSqSum(ByVal list As IEnumerable(Of Double)) As Double
    Return calcSqSum1(list)
End Function

etc

It's not quite what you're after, but the public functions will be type safe.

Upvotes: 1

Sergiu
Sergiu

Reputation: 129

You can use lambda expressions, like this:

        static T Add<T>(T a, T b)
    {
        // declare the parameters
        ParameterExpression paramA = Expression.Parameter(typeof(T), "a"),
            paramB = Expression.Parameter(typeof(T), "b");
        // add the parameters together
        BinaryExpression body = Expression.Add(paramA, paramB);
        // compile it
        Func<T, T, T> add = Expression.Lambda<Func<T, T, T>>(body, paramA, paramB).Compile();
        // call it
        return add(a, b);
    }

It will not be typesafe, but it will work for types that has the expected operator (addition, in the example above).

Upvotes: 1

Khalid Abuhakmeh
Khalid Abuhakmeh

Reputation: 10839

Sorry you can't unless you create your own number class.

public static T Add<T> (T x, T y) where T: MyNumberClass
{ 
// your add code
...
}

The reason is that .NET only lets you constrain a generic method with a class or an interface.

Upvotes: 1

Mehrdad Afshari
Mehrdad Afshari

Reputation: 422026

No, since there's no specific common interface that all of them implement. In essence, there's no real notion of "numerical types" in the framework. Unless you wrap them in self-defined classes and have your method accept only your types (which is not really a direct answer to your question, just a workaround).

Upvotes: 8

Related Questions