2012-12-05 43 views
3

我无法从搜索中找到答案,但也许我没有以正确的方式提问。我有一个程序,它有许多矩阵类型的函数,它们采用3D矩阵(以1D-2D锯齿形阵列的形式)并执行元素上的函数。但是我经常需要使用相同的函数,但使用不同类型的参数:int [] [,]和float [] [,]和double [] [,]。为多种类型的变量重载方法的更简单方法?

到目前为止,我只是重写了相同的方法,但改变了类型,但我有这些东西吨,这是一个真正的痛苦,不断重写“重新输入”的方法。

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

有没有更容易的方法来允许不同类型?如果有一种方法可以使用具有功能的泛型主方法(即3 for循环和其他主体代码),然后使用不同类型的辅助方法并为每种情况调用泛型方法,那将是非常好的。

谢谢大家。

回答

1

有一个在C#泛型没有运营商限制,所以我竟被下次使用:

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; 
    } 

对于这个解决方案可以创建几个类,但你可以支持任何操作(不仅是+)在这种情况下。

+0

请参阅Alexei的其他解决方案,该解决方案使用委托在相当少的代码中执行相同的操作。还要注意,正如我在答案中所说的那样,这两个解决方案都会增加一个相当明显的性能成本,这对于这些低级数学运算来说可能是不可接受的。 – Servy

+0

@Servy是的我同意,但正如我所提到的'对于这种解决方案,你可以创建更多的类,但是你可以在这种情况下支持任何运算符(不仅是+)。如果你必须使用 - ,/,*运算符来Math.Abs​​函数,你将不得不通过很多聚合器。 –

+0

@Serve关于函数调用也很好。我同意这可能会伤害表现,但它依赖于使用的痴呆症的数量。 –

2

不幸的是,对于这些基本的数字类型,并不是真的。他们没有实现的界面定义了+运算符。任何你尝试使用反射,动态,传递总和/聚合函数等都会导致相当大的性能成本。由于像这样的矩阵操作经常在性能密集的环境中进行,所以代码的减少很少值得推广这些方法。

+0

+1。性能考虑的好处。 –

+0

啊我明白了。那么我在这里提出的方法只是简单的例子。实际上,许多功能都充满了各种条件和对其他功能的调用。但我会检查表现;重写它们可能是值得的性能好处。谢谢! – superwillis

2

您可以使用泛型函数并传递委托来汇总和处理结果。正如Servy所说,代表的成本可能很大。你需要测量和看看下面的基本方法是否适合你。如果这些操作的性能很重要,保持单独的版本是很好的方法。您还可以检查使用Jon Skeet的MiscUtil对Generic operators的支持是否会给予可接受的性能。

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(在LINQ而言,其他共同的名字“减少”)是执行原来的“总和”类似Enumerable.Aggreagate更宽泛的版本与签名Func<TAccumulate, TSource, TAccumulate>postProcess只是摘要处理结果,代码可以通过调用通用版本​​(如果存在)来重写。对于原代码的用法是:

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

'SomeFunction ()'? – Zbigniew

+0

@des此方法适用于任何你需要的类型,只要你能想出一个你认为有意义的聚合函数。它基本上只是LINQ'Aggregate',但对于3D而不是1D数据结构,并且没有所有额外的重载类型。 – Servy

+0

嗯,现在我得到'Func 聚合'。 – Zbigniew

1

为什么不使用文本模板?

创建一个名为 '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#>); 
    } 

    <# } #> 
    } 
} 

当你保存它,突然迸出Overloads.cs - 与您的所有方法。

这与C++模板的工作方式类似。

有两点要注意:

线var desiredTypes = new[] ....创建字符串,然后将其用于驱动其内部产生的重载方法环路的阵列。

生成类为partial是很有意义的,所以你可以混入更传统的代码。

+0

哇!我不知道你可以做到这一点。绝对保存这个。谢谢 – superwillis

相关问题