2012-12-05 2 views
3

검색에서 답변을 찾을 수 없지만 올바른 방법을 묻지 않고 있습니다. 나는 (1D-2D 들쭉날쭉 한 배열의 형태로) 3D 행렬을 취하고 요소에 대한 함수를 수행하는 많은 행렬 형식 함수가있는 프로그램을 가지고 있습니다. 그러나 나는 종종 동일한 함수를 사용해야하지만, 형식이 다른 인수 인 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 개)이있는 일반 기본 메소드를 보유한 다음 다른 유형을 사용하고 각 사례에 대해 일반 메소드를 호출하는 헬퍼 메소드가있는 방법이 있으면 좋을 것입니다.

감사합니다.

답변

1

은 C#을 제네릭에는 연산자 제약 그래서 난 다음 사용하지 woul있다 : 당신이 몇 가지 더 클래스를 만들 수 있습니다

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 함수 호출에 대한 좋은 지적. 나는 그것이 성과를 해칠 수도 있지만 동의하는 데 메티오스의 숫자에 depens 동의합니다. –

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의 측면에서, 다른 일반적인 이름이 "감소")

서명 Func<TAccumulate, TSource, TAccumulate>Enumerable.Aggreagate 더 일반 버전과 비슷한 원래 "합"을 수행하는 것입니다. postProcess SomeFunctionB의 제네릭 버전을 호출하여 처리 결과 및 코드를 추상화하면 코드를 다시 작성할 수 있습니다. 원래 코드 사용을 위해 다음과 같습니다

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

'SomeFunction ()은 어떨까요? – Zbigniew

+0

@des이 방법은 의미있는 것으로 생각되는 집계 함수를 생각해 내면 필요한 모든 유형에서 작동합니다. 기본적으로 LINQ'Aggregate '이지만 1D 데이터 구조가 아닌 3D를위한 것이며 모든 추가 오버로드 유형은 없습니다. – Servy

+0

아아, 이제 Func 집합체를 얻습니다. – Zbigniew

1

왜 텍스트 템플릿을 사용하지 않습니까? - 당신의 방법에 당신이 그것을 저장

<#@ 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 팝 :

은 'Overloads.tt'라는 새로운 파일을 만듭니다.

이것은 C++ 템플릿의 작동 방식과 비슷합니다.

메모의 커플 :

라인 var desiredTypes = new[] .... 그러면 오버로드 된 메소드가 생성되는 내부 루프를 구동하는데 사용되는 문자열의 배열을 생성한다.

더 일반적인 코드를 혼합 할 수 있도록 클래스를 partial으로 생성하는 것이 좋습니다.

+0

워! 네가 할 수 있을지 모르겠다. 확실히 이것을 저장하십시오. 감사 – superwillis

관련 문제