使用 ref 和 out 传递数组
在项目中利用到了自己封装的一个FFT算法,这个算法需要返回两个数组值,因此考虑到了使用ref和out来传递数组。
1.ref和out的区别
- 数组类型的 ref 参数必须由调用方明确赋值(传入的参数必须先被初始化)。 因此,不需要由被调用方明确赋值。 可以将数组类型的 ref 参数更改为调用的结果。 例如,可以为数组赋以 null 值,或将其初始化为另一个数组。在使用数组类型的 out 参数前必须先为其赋值;即必须由被调用方(必须在方法中对其完成初始化)为其赋值。
- out适合用在需要retrun多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候。
2.实例
傅里叶变换的方法,返回两个数组,幅值和频率
//1.使用out参数
public void TTFTrans(out double[] range_data, out double[] frequency_data)
{
//**必须在方法中对其完成初始化**
range_data = new double[512];
frequency_data = new double[512];
double fs = 50;
Complex[] real_data = new Complex[1024];
Random r = new Random();
//采样1024个数据
for (int i = 0; i < 1024; i++)
{
real_data[i] = 15 * Math.Sin(Math.PI * 2 * 1 * (i / fs)) + 10 * Math.Sin(Math.PI * 2 * 5 * (i / fs)) + 5 * Math.Sin(Math.PI * 2 * 10 * (i / fs)) + r.Next(0, 20) * Math.Sin(Math.PI * 2 * r.Next(0, 10) * 20 * ((i) / fs));
}
Complex[] com = FFTT.fft_frequency(real_data);
//计算所得到幅度值
//对采样数组进行傅里叶变换
double N = com.Length;
for (int i = 0; i < com.Length / 2; i++)
{
double rr = (com[i].Real);
double ri = (com[i].Imaginary);
frequency_data[i] = i * (fs / N);
range_data[i] = (Math.Sqrt((rr * rr) + (ri * ri))) / (N / 2);
}
}
FFTT.cs:
public static class FFTT
{
public static Complex[] fft_frequency(Complex[] sourceData)
{
int countN = sourceData.Length;
//2的r次幂为N,求出r.r能代表fft算法的迭代次数
int r = Convert.ToInt32(Math.Log(countN, 2));
//分别存储蝶形运算过程中左右两列的结果
Complex[] interVar1 = new Complex[countN];
Complex[] interVar2 = new Complex[countN];
interVar1 = (Complex[])sourceData.Clone();
//w代表旋转因子
Complex[] w = new Complex[countN / 2];
//为旋转因子赋值。(在蝶形运算中使用的旋转因子是已经确定的,提前求出以便调用)
//旋转因子公式 \ /\ /k __
// \/ \/N -- exp(-j*2πk/N)
//这里还用到了欧拉公式
for (int i = 0; i < countN / 2; i++)
{
double angle = -i * Math.PI * 2 / countN;
w[i] = new Complex(Math.Cos(angle), Math.Sin(angle));
}
//蝶形运算
for (int i = 0; i < r; i++)
{
//i代表当前的迭代次数,r代表总共的迭代次数.
//i记录着迭代的重要信息.通过i可以算出当前迭代共有几个分组,每个分组的长度
//interval记录当前有几个组
// <<是左移操作符,左移一位相当于*2
//多使用位运算符可以人为提高算法速率^_^
int interval = 1 << i;
//halfN记录当前循环每个组的长度N
int halfN = 1 << (r - i);
//循环,依次对每个组进行蝶形运算
for (int j = 0; j < interval; j++)
{
//j代表第j个组
//gap=j*每组长度,代表着当前第j组的首元素的下标索引
int gap = j * halfN;
//进行蝶形运算
for (int k = 0; k < halfN / 2; k++)
{
interVar2[k + gap] = interVar1[k + gap] + interVar1[k + gap + halfN / 2];
interVar2[k + halfN / 2 + gap] = (interVar1[k + gap] - interVar1[k + gap + halfN / 2]) * w[k * interval];
}
}
//将结果拷贝到输入端,为下次迭代做好准备
interVar1 = (Complex[])interVar2.Clone();
}
//将输出码位倒置
for (uint j = 0; j < countN; j++)
{
//j代表自然顺序的数组元素的下标索引
//用rev记录j码位倒置后的结果
uint rev = 0;
//num作为中间变量
uint num = j;
//码位倒置(通过将j的最右端一位最先放入rev右端,然后左移,然后将j的次右端一位放入rev右端,然后左移...)
//由于2的r次幂=N,所以任何j可由r位二进制数组表示,循环r次即可
for (int i = 0; i < r; i++)
{
rev <<= 1;
rev |= num & 1;
num >>= 1;
}
interVar2[rev] = interVar1[j];
}
return interVar2;
}
}
客户端调用:
public MainWindow()
{
InitializeComponent();
////声明double类型数组,用来接收傅里叶变换返回的幅值
double[] range_data = null;//幅值
//声明double类型数组,用来接收傅里叶变换返回的频率
double[] frequency_data = null;//频率
//**传入参数不需要初始化**
TTFTrans(out range_data, out frequency_data);
for (int i = 0; i < range_data.Length; i++)
{
Console.WriteLine(range_data[i].ToString());
}
}
//2.使用ref参数
public void TTFTrans(ref double[] range_data,ref double[] frequency_data)
{
double fs = 50;
Complex[] real_data = new Complex[1024];
Random r = new Random();
//采样1024个点
for (int i = 0; i < 1024; i++)
{
real_data[i] = 15 * Math.Sin(Math.PI * 2 * 1 * (i / fs)) + 10 * Math.Sin(Math.PI * 2 * 5 * (i / fs)) + 5 * Math.Sin(Math.PI * 2 * 10 * (i / fs)) + r.Next(0, 20) * Math.Sin(Math.PI * 2 * r.Next(0, 10) * 20 * ((i) / fs));
}
Complex[] com = FFTT.fft_frequency(real_data);
//计算所得到幅度值
//对采样数组进行傅里叶变换
double N = com.Length;
for (int i = 0; i < com.Length / 2; i++)
{
double rr = (com[i].Real);
double ri = (com[i].Imaginary);
frequency_data[i] = i * (fs / N);
range_data[i] = (Math.Sqrt((rr * rr) + (ri * ri))) / (N / 2);
}
}
客户端:
public MainWindow()
{
InitializeComponent();
//**使用ref传入的参数必须初始化**
double[] range_data = new double[1024 / 2];//幅值
double[] frequency_data = new double[1024/2];//频率
TTFTrans( ref range_data,ref frequency_data);
for (int i = 0; i < range_data.Length; i++)
{
Console.WriteLine(range_data[i].ToString());
}
}
ref是有进有出,而out是只出不进