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ループやその他の本文コード)と、異なるタイプを取り、それぞれの場合に汎用メソッドを呼び出すヘルパーメソッドを作成する方法があれば素晴らしいと思います。

皆さんありがとう。

4

4 に答える 4

2

残念ながら、これらの基本的な数値タイプについては、実際にはそうではありません。+演算子を定義する、それらが実装するインターフェースはありません。リフレクション、動的、合計/集計関数の受け渡しなどを使用して実行しようとすると、かなりのパフォーマンスコストが発生します。このような行列演算は非常にパフォーマンスの高い環境で頻繁に行われるため、コードの削減がメソッドの一般化のコストに見合うことはめったにありません。

于 2012-12-05T17:28:44.207 に答える
2

ジェネリック関数を使用し、デリゲートを渡して結果を集約および処理できます。Servyが述べたように、代表者の費用はかなりの額になる可能性があります。以下のような基本的なアプローチがうまくいくかどうかを測定して確認する必要があります。これらの操作のパフォーマンスが重要な場合は、別々のバージョンを維持することをお勧めします。Jon Skeetのジェネリック演算子のMiscUtilサポートを使用すると、許容できるパフォーマンスが得られるかどうかを確認することもできます。

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用語では、他の一般名「reduce」)は、 Enumerable.Aggreagateと同様の元の「sum」を署名で実行することFunc<TAccumulate, TSource, TAccumulate>です。postProcess処理結果を抽象化するだけで、存在する場合はの汎用バージョンを呼び出すことでコードを書き直すことができますSomeFunctionB。元のコードの使用法は次のようになります。

 SomeFunctionA<float>((sum, current) => sum + current, SomeFunctionB);
于 2012-12-05T17:29:01.920 に答える
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;
    }

このソリューションでは、さらにいくつかのクラスを作成できますが、この場合は任意の演算子(+だけでなく)をサポートできます。

于 2012-12-05T17:37:28.703 に答える
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より伝統的なコードを混ぜることができるように、クラスを生成することは理にかなっています。

于 2012-12-05T17:51:07.627 に答える