C# 方法,该方法同时接受float[]和double[]数组

C# 方法,该方法同时接受float[]和double[]数组,c#,arrays,C#,Arrays,我有一个方法,它接受数组(float或double)、开始索引和结束索引,然后对startIndex到endIndex范围内的索引执行一些元素操作 基本上是这样的: public void Update(float[] arr, int startIndex, int endIndex) { if (condition1) { //Do some array manipulation } else if (condition2) { //Do s

我有一个方法,它接受数组(float或double)、开始索引和结束索引,然后对startIndex到endIndex范围内的索引执行一些元素操作

基本上是这样的:

public void Update(float[] arr, int startIndex, int endIndex)
{
   if (condition1)
   {
     //Do some array manipulation
   }
   else if (condition2)
   {
     //Do some array manipulation
   }
   else if (condition3)
   {
       if (subcondition1)
       {
         //Do some array manipulation
       }
   }
}
方法的长度比这个长,并且涉及将某些元素设置为0或1,或者规范化数组。 问题是我需要在那里传递
float[]
double[]
数组,并且不希望有一个接受
double[]
的重复代码

性能也是至关重要的,所以我不想创建一个新的
double[]
数组,将浮点数组转换为它,执行calcs,然后通过转换回浮点来更新原始数组


是否有任何解决方案可以避免重复代码,但速度也尽可能快?

如何使用泛型实现该方法?可以为核心业务逻辑创建抽象基类:

abstract class MyClass<T>
{
    public void Update(T[] arr, int startIndex, int endIndex)
    {
        if (condition1)
        {
            //Do some array manipulation, such as add operation:
            T addOperationResult = Add(arr[0], arr[1]);
        }
        else if (condition2)
        {
            //Do some array manipulation
        }
        else if (condition3)
        {
            if (subcondition1)
            {
                //Do some array manipulation
            }
        }
    }

    protected abstract T Add(T x, T y);
}
抽象类MyClass
{
公共无效更新(T[]arr、int startIndex、int ENDIX)
{
如果(条件1)
{
//执行一些数组操作,例如添加操作:
T addOperationResult=Add(arr[0],arr[1]);
}
否则如果(条件2)
{
//做一些数组操作
}
否则,如果(条件3)
{
如果(分条款1)
{
//做一些数组操作
}
}
}
保护摘要T加(tx,ty);
}
然后按数据类型实现一个优化为特定类型操作的继承类:

class FloatClass : MyClass<float>
{
    protected override float Add(float x, float y)
    {
        return x + y;
    }
}

class DoubleClass : MyClass<double>
{
    protected override double Add(double x, double y)
    {
        return x + y;
    }
}
类浮动类:MyClass
{
受保护的覆盖浮动添加(浮动x、浮动y)
{
返回x+y;
}
}
类别双重类别:MyClass
{
受保护覆盖双加(双x,双y)
{
返回x+y;
}
}

大多数代码的性能并不十分关键,因此花时间将
浮点值转换为
双精度值并返回会导致问题:

public void Update(float[] arr, int startIndex, int endIndex)
{
    double[] darr = new double[arr.Length];
    for(int i=startIndex; i<endIndex; i++)
        darr[i] = (double) arr[i];
    Update(darr, startIndex, endIndex);
    for(int j=startIndex; j<endIndex; j++)
        arr[j] = darr[j];
}
public void更新(float[]arr,int startIndex,int endIndex)
{
double[]darr=新的double[arr.Length];

对于(inti=startIndex;i您有几个选项。它们都不完全符合您的要求,但取决于您需要的操作类型,您可能会接近

第一种方法是使用泛型方法,其中泛型类型受到限制,但只能执行以下操作:

public void Update<T>(T[] arr, int startIndex, int endIndex) : IComarable
{
   if (condition1)
   {
     //Do some array manipulation
   }
   else if (condition2)
   {
     //Do some array manipulation
   }
   else if (condition3)
   {
       if (subcondition1)
       {
         //Do some array manipulation
       }
   }
}
这足以完成查找最小值、最大值或对数组中的项进行排序等操作。它无法执行加法/减法等操作,因此无法找到平均值。您可以通过为所需的任何其他方法创建自己的重载委托来弥补这一点:

public void Update<T>(T[] arr, int startIndex, int endIndex, Func<T,T> Add) : IComarable
{
   //...
   arr[Index] = Add(arr[OtherIndex] + arr[ThirdIndex]);
}
这应该行得通,但对于像你所说的一遍又一遍的事情,我不知道这会对表演产生什么影响

您可以将此选项与另一个答案(现已删除)结合使用,以返回某种类型安全性:

public void Update(float[] arr, int startIndex, int endIndex)
{
    InternalUpdate(arr, startIndex, endIndex);
}
public void Update(double[] arr, int startIndex, int endIndex)
{
    InternalUpdate(arr, startIndex, endIndex);
}
public void InternalUpdate(dynamic[] arr, int startIndex, int endIndex)
{
     //...logic here
}
另一个想法是将所有浮点数转换为双倍:

public void Update(float[] arr, int startIndex, int endIndex)
{
    Update( Array.ConvertAll(arr, x => (double)x), startIndex, endIndex);
}

public void Update(double[] arr, int startIndex, int endIndex)
{
   //...logic here
}
同样,这将重新分配阵列,因此如果这导致性能问题,我们将不得不去别处寻找

如果(且仅当)所有其他方法都失败了,探查器显示这是代码的一个关键性能部分,您只需重载该方法并实现两次逻辑即可。从代码维护的角度来看,这并不理想,但如果性能问题得到了很好的确定和记录,那么它可能是值得复制的t表示您可能希望如何记录此信息:

/******************
   WARNING: Profiler tests conducted on 12/29/2014 showed that this is a critical
            performance section of the code, and that separate double/float
            implementations of this method produced a XX% speed increase.
            If you need to change anything in here, be sure to change BOTH SETS,
            and be sure to profile both before and after, to be sure you
            don't introduce a new performance bottleneck. */

public void Update(float[] arr, int startIndex, int endIndex)
{
    //...logic here
}

public void Update(double[] arr, int startIndex, int endIndex)
{
    //...logic here
}

这里要探讨的最后一个问题是,C#包含一个泛型类型,您可能会发现它在这方面很有用。

只是一个想法。我不知道性能影响是什么,但这有助于我入睡:p

public void HardcoreWork(double[] arr){HardcoreWork(arr, null);}
public void HardcoreWork(float[] arr){HardcoreWork(null, arr);}

public struct DoubleFloatWrapper
{
    private readonly double[] _arr1;
    private readonly float[] _arr2;

    private readonly bool _useFirstArr;

    public double this[int index]
    {
        get {
            return _useFirstArr ? _arr1[index] : _arr2[index];
        }
    }

    public int Length
    {
        get {
            return _useFirstArr ? _arr1.Length : _arr2.Length;
        }
    }

    public DoubleFloatWrapper(double[] arr1, float[] arr2)
    {
        _arr1 = arr1;
        _arr2 = arr2;
        _useFirstArr = _arr1 != null;
    }
}

private void HardcoreWork(double[] arr1, float[] arr2){

    var doubleFloatArr = new DoubleFloatWrapper(arr1, arr2);
    var len = doubleFloatArr.Length;

    double sum = 0;

    for(var i = 0; i < len; i++){
        sum += doubleFloatArr[i];
    }
}
public void硬核制品(double[]arr){硬核制品(arr,null);}
公开作废的硬核制品(float[]arr){硬核制品(null,arr);}
公共结构DoubleFloatWrapper
{
专用只读双[]\u arr1;
私有只读浮点[]\u arr2;
私有只读bool_useFirstArr;
公共双本[int索引]
{
得到{
返回_useFirstArr?_arr1[index]:_arr2[index];
}
}
公共整数长度
{
得到{
返回_useFirstArr?_arr1.Length:_arr2.Length;
}
}
公共DoubleFloatWrapper(双[]arr1,浮点[]arr2)
{
_arr1=arr1;
_arr2=arr2;
_useFirstArr=\u arr1!=null;
}
}
专用空心硬芯制品(双[]arr1,浮动[]arr2){
var doubleFloatArr=新的DoubleFloatWrapper(arr1,arr2);
var len=双浮点数长度;
双和=0;
对于(变量i=0;i
不要忘记,如果您拥有的元素数量少得可笑,您可以使用池内存,这将使您的内存开销为零

ThreadLocal<double[]> _memoryPool = new ThreadLocal<double[]>(() => new double[100]);

private void HardcoreWork(double[] arr1, float[] arr2){

    double[] array = arr1;
    int arrayLength = arr1 != null ? arr1.Length : arr2.Length;

    if(array == null)
    {
        array = _memoryPool.Value;

        for(var i = 0; i < arr2.Length; i++)
            array[i] = arr2[i];
    }


    for(var i = 0; i < 1000000; i++){
        for(var k =0; k < arrayLength; k++){
            var a = array[k] + 1;
        }
    }
}
ThreadLocal\u memoryPool=newthreadlocal(()=>newdouble[100]);
专用空心硬芯制品(双[]arr1,浮动[]arr2){
双[]数组=arr1;
int arrayLength=arr1!=null?arr1.Length:arr2.Length;
if(数组==null)
{
数组=_memoryPool.Value;
对于(变量i=0;i约翰对宏的评论,虽然完全不准确地描述C++模板,但让我想到了预处理器。

C的预处理器远不如C的强大(C++继承),但是它仍然能够处理除了复制本身之外的所有东西:

partial class MyClass
{
#if FOR_FLOAT
    using Double = System.Single;
#endif

    public void Update(Double[] arr, int startIndex, int endIndex)
    {
        // do whatever you want, using Double where you want the type to change, and
        // either System.Double or double where you don't
    }
}
现在,您需要在项目中包含该文件的两个副本,其中一个具有额外的

#define FOR_FLOAT
顶部的行。(自动添加该行应该相当容易)

不幸的是,
/define
编译器选项适用于整个程序集,而不是每个文件,因此不能使用硬链接
partial class MyClass
{
#if FOR_FLOAT
    using Double = System.Single;
#endif

    public void Update(Double[] arr, int startIndex, int endIndex)
    {
        // do whatever you want, using Double where you want the type to change, and
        // either System.Double or double where you don't
    }
}
#define FOR_FLOAT