如何在C#中使用预定义的类代理

时间:2016-11-02 01:20:31

标签: c# unity3d delegates audioclip

我对编程很新,目前正在尝试学习如何在C#中使用委托。我已经在MSDN上阅读了C#delegates编程指南,并查看了Stack Overflow上的其他一些示例。我认为我得到了整体概念,但我对如何使用已在类中定义的委托感到困惑。例如,在Unity AudioClip类中,有一个名为PCMReaderCallback的委托,每次音频剪辑读取信息时都会调用该委托。它所采用的参数只是一个float值的数组。

public delegate void PCMReaderCallback(float[] data);

我猜这意味着我可以使用这个委托来包装任何使用float数组作为参数的方法。在我经历过的教程中,他们解释了如何创建一个包含您在定义委托时选择的方法的委托,这对我没有帮助,因为PCMReaderCalled已经在{{1}中定义了} class。

我的问题是如何使用已经定义的委托来调用我选择的方法?

也许这是不可能的,或者我首先对代表的目的感到困惑。

2 个答案:

答案 0 :(得分:2)

您有一个委托声明为:

public delegate void PCMReaderCallback(float[] data);

然后你有一个Unity AudioClip.Create函数,它使用这个委托作为参数。这是唯一要理解的内容。

这就是它的样子:

public static AudioClip Create(string name, int lengthSamples, int channels, int frequency, bool stream, PCMReaderCallback pcmreadercallback);

您可以看到,它需要PCMReaderCallback作为参数,这是上面代表的名称。

现在,为了使用它,首先要创建一个与委托参数匹配的函数。请记住,我们的委托将float[]作为参数,并且是void返回类型。你命名这个函数真的没关系。它应该如下所示:

void OnAudioRead(float[] data)
{

}

最后,要使用该功能:

AudioClip newAudioClip = AudioClip.Create("Pigeon", samplerate * 2, 1, samplerate, true, OnAudioRead);
AudioSource attachedSource = GetComponent<AudioSource>();
attachedSource.clip = newAudioClip;
attachedSource.Play();

您可以看到,我们将OnAudioRead函数传递给PCMReaderCallback pcmreadercallback参数,该参数会在每次OnAudioRead读取数据时调用我们的AudioClip函数。这是Unity自动调用的。

  

我的问题是如何使用已经存在的委托   定义为调用我选择的方法?

我们以PCMReaderCallback为例。 PCMReaderCallback在名为AudioClip的类中声明。要使用它,您必须使用全名AudioClip.PCMReaderCallback

创建一个以AudioClip.PCMReaderCallback为参数,进行一些数据处理,然后使用Invoke调用处理完成时传入的函数的函数:

void makeAlecAudioFunction(AudioClip.PCMReaderCallback allecCallBack)
{
    //Generate some random dummy audio data
    float[] dummyData = new float[4000];
    for (int i = 0; i < dummyData.Length; i++)
    {
        dummyData[i] = Random.Range(0f, 1f);
    }

    //Call the function that was passed in then pass it in the data we generated
    allecCallBack.Invoke(dummyData);
}

该功能的使用

创建一个在makeAlecAudioFunction处理完数据后将被调用的函数。

void OnAlecAudio(float[] data)
{
    for (int i = 0; i < data.Length; i++)
    {
        Debug.Log("Alec Audio Data: " + data[i]);
    }
}

现在,要调用makeAlecAudioFunction函数,请调用它并传入OnAlecAudio函数。请记住,他们的参数必须匹配!:

makeAlecAudioFunction(OnAlecAudio);

最后,我认为回调函数背后的主要原因是在不使程序的其余部分等待的情况下执行某些操作,然后在该操作完成时执行回调。因此,您的makeAlecAudioFunction应该是协程函数,或者应该在另一个Thread中调用(在Unity中很复杂)。如果使用Thread,则必须在主Thread中调用回调。试着保持这个例子的简单。

答案 1 :(得分:1)

委托是您(或其他人)定义的方法描述类型。如果您熟悉Action和Func类型。它们是定义作为参数传递的预定义方法类型的委托。

例如,您的类型PCMReaderCallback(float[] data)可以在如下方法中使用:

public void ProcessData(PCMReaderCallback callback)
{
   List<float> data = new List<float>();
   // generate data here, or load it, etc
   // Then pass the data to the callback.
   callback(data.ToArray());
} 

所以现在ProcessData可以采用该签名的各种方法。例如,

public void LogData(float[] data)
{
   // write the data to a log file
}

另一种方法

public void PrintData(float[] data)
{
   foreach(var d in data)
      Console.WriteLine(d.ToString());
}

你将ProcessData称为......

ProcessData(LogData); // or...
ProcessData(PrintData);

委托也用于创建事件,然后可以订阅。看看活动和代表。但事件可能是,

public event PCMReaderCallback DataRead;

然后在read方法中,我们让全世界都知道数据已被读取,任何订阅事件DataRead的人都可以使用它做任何事情。像...

// read data here
// Then pass to event, first check to see if we have any subscribers.
if (DataRead != null)
{
   // Then prevent race conditions (subscribes and unsubscribes while processing events)
   var Event = DataRead;
   Event(data); // Then call the event here
}