superwillis
superwillis

Reputation: 139

Simpler way to overload a method for a variable with multiple types?

I couldn't find the answer from searching, but maybe I'm not asking it the right way. I have a program with lots of matrix-type functions that take in 3D matrices (in the form of 1D-2D jagged arrays) and performs functions on elements. However I often need to use the same function but with differently typed arguments: int[][,] and float[][,] and double[][,].

So far I've just been rewriting the same method but changing the type, but I have tons of these things and it's a real pain to keep rewriting "re-typed" methods.

private float SomeFunctionA(float[][,] d)
{
    float sum = 0; 
    for (int k = 0; k < d.GetLength(0); k++)
        for (int j = 0; j < d[0].GetLength(1); j++)
            for (int i = 0; i < d[0].GetLength(0); i++)
                 sum += d[k][i,j];
    return SomeFunctionB(sum);
}

private float SomeFunctionA(double[][,] d)
{
    double sum = 0; 
    for (int k = 0; k < d.GetLength(0); k++)
        for (int j = 0; j < d[0].GetLength(1); j++)
            for (int i = 0; i < d[0].GetLength(0); i++)
                 sum += d[k][i,j];
    return SomeFunctionB(sum);
}

Is there an easier way to allow different types? It would be great if there was a way to have a generic main method with the functionality (i.e. the 3 for loops and other body code), and then helper methods which take a different type and call the generic method for each case.

Thanks all.

Upvotes: 3

Views: 790

Answers (4)

Andras Zoltan
Andras Zoltan

Reputation: 42363

Why not use text templating?

Create a new file called 'Overloads.tt':

<#@ template debug="false" hostspecific="false" language="C#" #>
<#@ assembly name="System.Core" #>
<#@ import namespace="System.Linq" #>
<#@ import namespace="System.Text" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ output extension=".cs" #>
using System;

namespace MyNamespace 
{
  public partial class Generated 
  {
  <# 
    var desiredTypes = new[] { "int", "float", "double" };
    foreach(var type in desiredTypes) {
  #>
  private <#=type#> SomeFunctionA(<#=type#>[][,] d)
  {
      <#=type#> sum = 0; 
      for (int k = 0; k < d.GetLength(0); k++)
          for (int j = 0; j < d[0].GetLength(1); j++)
              for (int i = 0; i < d[0].GetLength(0); i++)
                    sum += d[k][i,j];
      return SomeFunctionB(sum);
  }

  private <#=type#> SomeFunctionB(<#=type#> input)
  {
    return default(<#=type#>);
  }

  <# } #>
  }
}

When you save it, out pops Overloads.cs - with all your methods.

This is similar to how C++ templates work too.

A couple of notes:

The line var desiredTypes = new[] .... creates an array of strings which is then used to drive the loop inside which your overloaded methods are generated.

It makes sense to generate the class as partial so you can mix-in the more traditional code.

Upvotes: 1

Alexei Levenkov
Alexei Levenkov

Reputation: 100555

You can use generic function and pass delegates to aggregate and handle result. As Servy mentioned the cost of delegates may be significant. You need to measure and see if basic approach like below would work for you. If performance of these operations is important keeping separate versions is fine approach. You can also check if using Jon Skeet's MiscUtil support for Generic operators will make give acceptable performance.

private T SomeFunctionA<T>(T[][,] d, Func<T, T, T> aggregate, Func<T,T> postProcess)
{
    T sum = default(T); 
    for (int k = 0; k < d.GetLength(0); k++)
        for (int j = 0; j < d[0].GetLength(1); j++)
            for (int i = 0; i < d[0].GetLength(0); i++)
                 sum = aggreagate(sum, d[k][i,j]);
    return postProcess(sum);
}

aggregate (in LINQ terms, other common name "reduce") is to perform original "sum" similar to Enumerable.Aggreagate more generic version with signature Func<TAccumulate, TSource, TAccumulate>. postProcess just abstracts handling result and code can be rewritten by calling generic version of SomeFunctionB if one exists. For original code usage would be:

 SomeFunctionA<float>((sum, current) => sum + current, SomeFunctionB);

Upvotes: 2

petro.sidlovskyy
petro.sidlovskyy

Reputation: 5103

There is no operator constraints in C# generics so I woul use next:

    private interface ICalculator<T>
    {
        T Add(T x, T y);
        // you may want to add more operations here
    }

    private class Int32Calculator: ICalculator<int>
    {
        public int Add(int x, int y)
        {
            return x + y;
        }
    }

    private int Int32SomeFunction(int [][,] d)
    {
        return SomeFunction<int>(d, new Int32Calculator());
    }

    private T SomeFunction<T>(T[][,] d, ICalculator<T> calculator)
        where T : struct 
    {
        T sum = default(T);
        for (int k = 0; k < d.GetLength(0); k++)
            for (int j = 0; j < d[0].GetLength(1); j++)
                for (int i = 0; i < d[0].GetLength(0); i++)
                    sum = calculator.Add(sum, d[k][i, j]);
        return sum;
    }

For this solution you may create few more classes but you may support any operator (not only +) in this case.

Upvotes: 1

Servy
Servy

Reputation: 203835

Unfortunately, for these fundamental numeric types, not really. There's no interface they implement that defines the + operator. Anything that you try to do using reflection, dynamic, passing in a summation/aggregate function, etc. will result in a pretty significant performance cost. Since matrix operations such as this are so frequently done in very performance intensive environments the reduction in code is rarely worth the cost of generalizing the methods.

Upvotes: 2

Related Questions