Reputation: 139
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
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
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
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
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