我是编程新手,目前正在尝试学习如何在C#中使用委托。我已经阅读了MSDN上的C#委派编程指南,并查看了Stack Overflow上的其他一些示例。我想我已经了解了整个概念,但对如何使用类中已经定义的委托感到困惑。例如,在Unity AudioClip
类中,有一个名为PCMReaderCallback
的委托,每当音频剪辑读取信息时都会调用它。它所采用的参数只是float
值的一个数组。
public delegate void PCMReaderCallback(float[] data);
我猜这意味着我可以使用这个委托来包装任何使用浮点数组作为参数的方法。在我看过的教程中,它们解释了如何创建一个委托,该委托封装了您在定义委托时选择的方法,这对我没有帮助,因为PCMReaderCalled
back已经在AudioClip
类中定义了。
我的问题是,如何使用已经定义的委托来调用我选择的方法?
也许这是不可能的,也许我首先对代表们的目的感到困惑。
您有一个委托声明为:
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
参数,该参数将在每次AudioClip
读取数据时调用OnAudioRead
函数。这是Unity自动调用的。
我的问题是如何使用已经定义为调用我选择的方法?
让我们以PCMReaderCallback
为例。CCD_ 15是在一个称为CCD_ 16的类中声明的。要使用它,必须使用全名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
中调用回调。只是想让这个例子保持简单。
委托是由您(或其他人(定义的方法描述的类型。如果您熟悉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;
然后在稍后的读取方法中,我们让世界知道数据已经被读取,任何订阅了事件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
}