【问题标题】:Difficulty creating classes and constructors C#难以创建类和构造函数 C#
【发布时间】:2012-10-05 03:35:18
【问题描述】:

我正在尝试为我在学校做的一个项目实施离散傅立叶变换算法。但是创建一个类似乎很困难(不应该如此)。 我正在使用 Visual Studio 2012。

基本上我需要一个名为 Complex 的类来存储我从 DFT 获得的两个值;实部和虚部。

这就是我目前所拥有的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SoundEditor_V3
{
    public class Complex
    {
        public double real;
        public double im;

        public Complex()
        {
            real = 0;
            im = 0;
        }
    }
}

问题是它不承认构造函数是构造函数,我只是在学习C#,但是我在网上查了一下,它应该是这样的。 但它将我的构造函数识别为方法。

这是为什么呢? 我创建的课程有误吗?

我的傅立叶课也是如此。所以每次我尝试创建一个 傅里叶对象,然后使用它的方法......没有这样的事情。

例如,我这样做:

Fourier fou = new Fourier();
fou.DFT(s, N,  amp, 0);

它告诉我 fou 是一个“字段”,但用作“类型” 为什么这么说?

这也是我的傅立叶类的代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SoundEditor_V3
{
   public class Fourier
   {

        //FOURIER
        //N = number of samples
        //s is the array of samples(data)
        //amp is the array where the complex result will be written to
        //start is the where in the array to start
        public void DFT(byte[] s, int N, ref Complex[] amp, int start)
        {
            Complex tem = new Complex();
            int f;
            int t;

            for (f = 0; f < N; f++)
            {

                tem.real = 0;
                tem.im = 0;

                for (t = 0; t < N; t++)
                {
                    tem.real += s[t + start] * Math.Cos(2 * Math.PI * t * f / N);
                    tem.im -= s[t + start] * Math.Sin(2 * Math.PI * t * f / N);

                }
                amp[f].real = tem.real;
                amp[f].im = tem.im;

            }
        }

        //INVERSE FOURIER
        public void IDFT(Complex[] A, ref int[] s)
        {
            int N = A.Length;
            int t, f;
            double result;
            for (t = 0; t < N; t++)
            {
                result = 0;
                for (f = 0; f < N; f++)
                {
                    result += A[f].real * Math.Cos(2 * Math.PI * t * f / N) - A[f].im * Math.Sin(2 * Math.PI * t * f / N);
                }
                s[t] = (int)Math.Round(result);
            }
        }
    }
}

我现在非常困惑,任何和所有的帮助都将不胜感激。谢谢。

编辑:

这是我尝试访问所有课程的地方:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace SoundEditor_V3
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }


        string filename;
        NAudio.Wave.WaveStream waveStream;

        private NAudio.Wave.DirectSoundOut sout = null;

        private void openToolStripMenuItem_Click(object sender, EventArgs e)
        {

            OpenFileDialog open = new OpenFileDialog();
            open.Filter = "Wave File (*.wav)|*.wav";
            if (open.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            waveStream = new NAudio.Wave.WaveFileReader(open.FileName);
            filename = open.FileName;
            sout = new NAudio.Wave.DirectSoundOut();
            sout.Init(new NAudio.Wave.WaveChannel32(waveStream));

        }

        //Play
        private void Play_btn_Click(object sender, EventArgs e)
        {
            sout.Play();
        }

        //Stop
        private void Stop_btn_Click(object sender, EventArgs e)
        {
            sout.Stop();
            waveStream.Position = 0;
        }

        //Pause
        private void Pause_btn_Click(object sender, EventArgs e)
        {
            sout.Pause();
        }



        //display fourier
        //N = number of samples(length of array)
        //s is the array of samples(data)
        //amp is the array where the complex result will be written to
        //start is the where in the array to start
        static int N = 8;
        byte[] s = {1,2,3,4,5,6,7,8};


        Complex[] amp = new Complex[N];

        Fourier xfo = new Fourier();



        //xfo.DFT(s, N,  amp, 0); 

    }
}

【问题讨论】:

  • 向我们展示amp的声明
  • 另外,这可能看起来很奇怪,但是发布整个文件以供您使用类而不是这两行。文件中有一些非常愚蠢的拼写错误会导致这些错误。
  • 这些行是一个方法吗?如果您尝试在方法之外的类主体中编写该代码,则可能会出现该错误。
  • 并确保在传递 amp 参数时使用 ref 关键字
  • 这是我对 amp 的声明,虽然很可能是错误的。 Complex[] amp = new Complex[N];

标签: c# class constructor visual-studio-2012


【解决方案1】:

这个调用应该在一个方法中。到目前为止,它看起来直接在一个类之下。

//xfo.DFT(s, N,  amp, 0);

还为amp 添加ref。 (因为void DFT(...., ref Complex[] amp,....) 采用ref amp 参数。

xfo.DFT(s, N, ref amp, 0);

【讨论】:

  • 我同意内部方法作为正确答案,但不需要参考。根据代码,它没有添加任何内容。
  • @btlog 查看我的更新。 void DFT(..) 采用“ref Complex[] amp”参数。
【解决方案2】:

哦,男孩,还有很大的改进空间。

首先你使用类Complex作为一个结构体,实际上它不需要是一个类,所以让它成为一个结构体:

public struct Complex
{
    public double Imaginary;
    public double Real;
}

不需要构造函数,默认构造函数(编译器添加)将根据字段类型将字段设置为其默认值,对于double,默认值为0.0(这是您分配给它们的值无论如何*)。

我也将 im 重命名为 Imaginary,不要告诉我你必须输入更多,因为你有智能感知。如果你没有去下载 Mono Develop 或 Visual Studio Express。哦,我能感觉到你的想法:我们不应该依赖工具。嗯,没错,这也是编写易于阅读的代码的另一个原因,即使对于那些不熟悉这些概念的人也是如此(它也使搜索更容易)。

*:我想注意0 是一个整数字面量,0.0 是双精度值,但编译器会重新调整它并优化转换,因此在实际用途中是相同的。

让我们转到您的傅立叶类,首先是方法 DFT,我将其复制到下面(将 Complex 的字段名称重命名):

    //FOURIER
    //N = number of samples
    //s is the array of samples(data)
    //amp is the array where the complex result will be written to
    //start is the where in the array to start
    public void DFT(byte[] s, int N, ref Complex[] amp, int start)
    {
        Complex tem = new Complex();
        int f;
        int t;

        for (f = 0; f < N; f++)
        {

            tem.real = 0;
            tem.im = 0;

            for (t = 0; t < N; t++)
            {
                tem.real += s[t + start] * Math.Cos(2 * Math.PI * t * f / N);
                tem.im -= s[t + start] * Math.Sin(2 * Math.PI * t * f / N);
            }
            amp[f].real = tem.real;
            amp[f].im = tem.im;

        }
    }

首先要注意的是,您说 N 是样本数,s 是样本数组。好吧,如果您有一个数组,您可以查询数组的大小,即使您只想允许处理数组的一部分(我认为您想要),这也是一个好主意。但是,真的是 N 和 s 吗?

看,这就像魔术:

   //FOURIER
   //amp is the array where the complex result will be written to
   //start is the where in the array to start
   public void DFT(byte[] samples, int samplesCount, ref Complex[] amp, int start)
   {
       Complex tem = new Complex();
       int f;
       int t;

       for (f = 0; f < samplesCount; f++)
       {

           tem.Real = 0;
           tem.Imaginary = 0;

           for (t = 0; t < samplesCount; t++)
           {
               tem.Imaginary += samples[t + start] * Math.Cos(2 * Math.PI * t * f / samplesCount);
               tem.Imaginary -= samples[t + start] * Math.Sin(2 * Math.PI * t * f / samplesCount);
           }
           amp[f].Real = tem.Real;
           amp[f].Imaginary = tem.Imaginary;
       }
   }

好的,接下来你说 amp 是输出。那么如果是输出,为什么不让方法返回呢?

砰!

   //FOURIER
   //start is the where in the array to start
   Complex[] DFT(byte[] samples, int samplesCount, int start)
   {
       var = new Complex[samplesCount];
       Complex tem = new Complex();
       int f;
       int t;
       for (f = 0; f < samplesCount; f++)
       {

           tem.Real = 0;
           tem.Imaginary = 0;

           for (t = 0; t < samplesCount; t++)
           {
               tem.Imaginary += samples[t + start] * Math.Cos(2 * Math.PI * t * f / samplesCount);
               tem.Imaginary -= samples[t + start] * Math.Sin(2 * Math.PI * t * f / samplesCount);
           }
           result[f].Real = tem.Real;
           result[f].Imaginary = tem.Imaginary;
       }
       return result;
   }

它真的需要是一个数组吗?我认为这是使用yield 关键字并返回IEnumerable&lt;Complex&gt; 的好机会。但实际上,我会接受你想要的数组。

现在,您可能不想返回一个数组。可能您只想修改预先存在的数组的一部分。在这种情况下,您应该开始检查您的界限。即使那是真的,你根本不需要 ref !因为数组是引用类型。它是按值传递的引用,如果您不能完全理解这个想法,请相信我,您可以修改数组的内容并在没有引用的情况下看到它反映在外部...按引用传递引用允许您更改被另一个人引用,而你没有这样做。

演示:

void Main()
{
    var x = new int[1];
    Do(x);
    Console.WriteLine(x);
}

void Do (int[] array)
{
    array[0] = 1;
}

上一个程序(用 LinqPad 编译)的输出是“1”。

但是让我们回到你的代码,好吗?

我不知道ft 是什么。谢天谢地,我知道我是虚构的(它是,对吧?)。所以我不会重命名它们。但我会将它们的定义移到循环中:

   Complex[] DFT(byte[] samples, int samplesCount, int start)
   {
       var result = new Complex[samplesCount];
       Complex tem = new Complex();
       for (int f = 0; f < samplesCount; f++)
       {
           tem.Real = 0;
           tem.Imaginary = 0;
           for (int t = 0; t < samplesCount; t++)
           {
               tem.Imaginary += samples[t + start] * Math.Cos(2 * Math.PI * t * f / samplesCount);
               tem.Imaginary -= samples[t + start] * Math.Sin(2 * Math.PI * t * f / samplesCount);

           }
           result[f].Real = tem.Real;
           result[f].Imaginary = tem.Imaginary;
       }
       return result;
   }

注意我使用了var 关键字。有了它,编译器将变量的类型分配给我用来初始化它的类型。所以在这种情况下,结果是Complex[],但我不必在代码中写两次。

最后,复制Complex 对象内容的那部分,好吧,我也会更改它。为什么?因为 Complex 现在是一个结构体,而结构体是值类型。所以它的内容被复制而不是引用。

   //FOURIER
   //start is the where in the array to start
   Complex[] DFT(byte[] samples, int samplesCount, int start)
   {
       var result = new Complex[samplesCount];
       Complex tem = new Complex();
       for (int f = 0; f < samplesCount; f++)
       {
           tem.Real = 0;
           tem.Imaginary = 0;
           for (int t = 0; t < samplesCount; t++)
           {
               tem.Imaginary += samples[t + start] * Math.Cos(2 * Math.PI * t * f / samplesCount);
               tem.Imaginary -= samples[t + start] * Math.Sin(2 * Math.PI * t * f / samplesCount);

           }
           result[f] = tem;
       }
       return result;
   }

我知道您真的只想处理数组的一部分。但请耐心等待……你会学到一些东西,而且这些代码无论如何都会有用的。

接下来我想要返回一个IEnumerable&lt;Complex&gt;,它是一个接口,表示可以迭代以获取复杂类型对象的任何内容。我还将使用yield 关键字。

此外,我已经摆脱了 sampleCount 并改用 samples.Length。

看看它有多漂亮:

    //FOURIER
    public IEnumerable<Complex> DFT(byte[] samples, int startIndex)
    {
        int samplesLength = samples.Length;
        for (int f = 0; f < samplesLength; f++)
        {
            Complex resultItem = new Complex();
            for (int t = 0; t < samplesLength; t++)
            {
                resultItem.Real += samples[t + startIndex] * Math.Cos(2 * Math.PI * t * f / samplesLength);
                resultItem.Imaginary -= samples[t + startIndex] * Math.Sin(2 * Math.PI * t * f / samplesLength);
            }
            yield return resultItem;
        }
    }

事实上,我也会去掉 startIndex(反正我们不会检查边界*)。

*:也就是说,我们不检查索引是否在数组大小内。我知道,我知道,您稍后会添加它们...可能。

不管怎样,你在这里学习了一些 C#。

    //FOURIER
    public IEnumerable<Complex> DFT(byte[] samples)
    {
        int samplesLength = samples.Length;
        for (int f = 0; f < samplesLength; f++)
        {
            Complex resultItem = new Complex();
            for (int t = 0; t < samplesLength; t++)
            {
                resultItem.Real += samples[t] * Math.Cos(2 * Math.PI * t * f / samplesLength);
                resultItem.Imaginary -= samples[t] * Math.Sin(2 * Math.PI * t * f / samplesLength);
            }
            yield return resultItem;
        }
    }

嗯,接下来困扰我的事情是傅立叶类没有状态(它没有字段,或者任何值被持久化的变量......不知何故)。所以,让它成为一个带有静态方法的静态类:

public static class Fourier
{
    //FOURIER
    public static IEnumerable<Complex> DFT(byte[] samples)
    {
        int samplesLength = samples.Length;
        for (int f = 0; f < samplesLength; f++)
        {
            Complex resultItem = new Complex();
            for (int t = 0; t < samplesLength; t++)
            {
                resultItem.Real += samples[t] * Math.Cos(2 * Math.PI * t * f / samplesLength);
                resultItem.Imaginary -= samples[t] * Math.Sin(2 * Math.PI * t * f / samplesLength);
            }
            yield return resultItem;
        }
    }
}

您当然注意到我没有添加 IDFT。那是家庭作业。

现在,让我们看看你如何使用它。就我而言,我创建了一个 ConsoleApplication,只是为了让它快速启动并运行(不浪费时间设计 GUI)。

我想要的是调用 Fourier.DFT,因为它是静态的,所以我可以不使用 Fourier 类型的对象(实际上我无法创建 Fourier 类型的对象,因为它是静态的)。

此方法接收byte[] 类型的参数。那个将是new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }。该方法将返回一些我可以用来迭代以获取 Complex 类型的对象的东西。所以我想把它放在一个循环中。

这就是我的代码的样子:

class Program
{
    static void Main(string[] args)
    {
        //display fourier
        foreach (var item in Fourier.DFT(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8 }))
        {
            Console.WriteLine(item);
        }
    }
}

现在...输出是...鼓声...

好吧,我看不到它,我忘记了Console.ReadLine();,但添加后输出是......

Namespace.Complex
Namespace.Complex
Namespace.Complex
Namespace.Complex
Namespace.Complex
Namespace.Complex
Namespace.Complex
Namespace.Complex

等等,什么?碰巧我还没有告诉它如何将 Complex 类型的对象转换为字符串。所以让我们补充一下:

public struct Complex
{
    public double Imaginary;
    public double Real;

    public override string ToString()
    {
        return string.Format("Complex [Real: {0}, Imaginary: {1}]", Real, Imaginary);
    }
}

现在我的输出是:

Complex [Real: 36, Imaginary: 0]
Complex [Real: -4, Imaginary: 9,65685424949238]
Complex [Real: -4, Imaginary: 4]
Complex [Real: -4, Imaginary: 1,65685424949239]
Complex [Real: -4, Imaginary: -3,91874033223161E-15]
Complex [Real: -4,00000000000001, Imaginary: -1,65685424949239]
Complex [Real: -4,00000000000002, Imaginary: -4,00000000000001]
Complex [Real: -3,99999999999997, Imaginary: -9,65685424949237]

输出是否正确?我没有什么鬼主意!我必须更多地了解傅立叶(但它看起来很合法)。

经过验证,输出正确。


最后一点:使用调试器逐步检查代码,您可能会发现惊喜(提示:yield)。

【讨论】:

    【解决方案3】:

    你需要一个方法,试试这个:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace SoundEditor_V3
    {
        public class Complex
        {
            public double real;
            public double im;
    
            public Complex()
            {
                real = 0;
                im = 0;
            }
    
            public Setval (double newReal, double newIm)
            {
                real = newReal;
                im = newIm;
            }
        }
    }
    

    关于你的其他类,构造函数在哪里? ;)

    【讨论】:

    • 复杂 tem = new Complex();只是初始化 Complex 类并将 type 和 im 变量设置为 0。使用上面发布的方法,而不是 tem.real = 0; tem.im = 0;只需替换为 tem(0,0);。编辑:更好的方法是创建 getter/setter,以便您可以在 Fourier 类中执行计算。
    • 好吧,它应该是Public Complex() { real = 0; im = 0;},但即使没有构造函数声明也会发生此错误。也许我让构造函数错了?
    • 对不起,我提到的 tem(0,0),实际上应该是 tem.SetVal(0,0)。我编辑了上面的代码以反映更改。
    • 不用担心。我最终确实在傅立叶类中进行了我需要的计算,但是我需要对 DFT 给我的值做 pythagorous。所以我需要将它们存储在 Complex 中,但我需要创建一个 Complex 对象数组。我之前用的是结构体,好像还可以,但是老师坚持把Complex作为一个类来实现会更好
    • @LuciferFayte 所以你实际上需要一个数组。无论如何,考虑一下,您使用的是 Visual Stuido 2012...然后您可以使用在 .NET 4.0 中添加的 System.Numerics.Complex(它是一个结构)。
    【解决方案4】:

    感谢您的所有帮助;我实际上最终弄清楚了一切。 我无法访问我试图访问它们的区域中的方法。我不得不将它们放在一个方法块中,因为这一切都被编码在一个表单中。 反正这是我的理解。

    但再次感谢您的所有建议,它们都很有帮助。

    【讨论】:

      猜你喜欢
      • 2015-01-07
      • 2013-04-29
      • 2012-02-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-08
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多