Подписка на нужный делегат в зависимости от переданного параметра
Надеюсь, корректно отобразил проблему в заголовке. Дайте знать, если нужно переписать её.
Возник вопрос в правильности моего кода. Суть в том, что у меня существует 2 метода - синхронный и синхронный, который под капотом делит List на несколько и выполняет обработку в нескольких потоках, который задаётся в конструкторе в зависимости от переданных параметров.
private delegate Image FaceMethodDelegate(object sender, NewFrameEventArgs e);
private FaceMethodDelegate_syncFaceDelegate;
private FaceMethodDelegate_asyncFaceDelegate;
public Image CameraImage { get; private set; }
public DetectFace(MethodChoiceEnum methodType)
{
_syncFaceDelegate = Device_NewFrame;
_asyncFaceDelegate = Device_NewFrame_Parallel;
_device.NewFrame += (sender, e) =>
{
CameraImage = methodType is MethodChoiceEnum.Sync
? _syncFaceDelegate(sender, e)
: _asyncFaceDelegate(sender, e);
};
}
Далее через метод Start запускается нужный метод.
public void Start() =>
_device.Start();
Используются библиотеки EmguCv + AForge.
Собственно, ещё раз вопрос - является ли корректным данный выбор метода или стоит как-то переписать данных код? Оба метода возвращают Image
UPD:
_syncFaceDelegate:
private Image Device_NewFrame(object sender, NewFrameEventArgs e)
{
foreach (var f in facesDetectedNow.First())
{
if (**)
{
//somecode
break;
}
}
}
_asyncFaceDelegate:
private Image Device_NewFrame_Parallel(object sender, NewFrameEventArgs e)
{
Parallel.ForEach(facesDetectedNow[0], (f, state) =>
if (**)
{
//somecode
state.Stop();
});
}
Ответы (1 шт):
Ваш способ корректный (при условии, что оба метода и правда возвращают Image), но странный.
Для начала, у вас тут лишние делегаты. Нет никакого смысла в делегате, которому вы присваиваете конкретный метод и больше ничего - проще вызывать этот метод напрямую:
CameraImage = methodType is MethodChoiceEnum.Sync
? Device_NewFrame(sender, e)
: Device_NewFrame_Parallel(sender, e);
Далее, вы делаете выбор между методами в момент срабатывания события, в то время как критерий-то выбора от события с событию не меняется. А значит, можно сэкономить немного времени процессора если сделать выбор заранее, на моменте подписки:
switch (methodType) {
case MethodChoiceEnum.Sync:
_device.NewFrame += Device_NewFrame;
break;
case MethodChoiceEnum.Parallel:
_device.NewFrame += Device_NewFrame_Parallel;
break;
}
PS меня настораживает название вашего класса - такое ощущение что вы его сделали там, где было бы достаточно метода. Этот класс мог бы быть частью ViewModel, если бы у него было событие CameraImageChanged или реализация INotifyPropertyChanged, но я их не вижу. Возможно, вы просто выкинули лишние детали чтобы получить MCVE, в таком случае всё в порядке. Но если он и правда такой небольшой, то надо с ним что-то делать.
Если смысл класса лишь в выборе между двумя функциями, то вместо него можно было бы использовать статический метод:
static FaceMethodDelegate SelectFaceImageProcessor(MethodChoiceEnum methodType) {
switch (methodType) {
case MethodChoiceEnum.Sync:
return Device_NewFrame;
case MethodChoiceEnum.Parallel:
return Device_NewFrame_Parallel;
default:
throw …;
}
}
Если же смысл класса в том, чтобы быть однократным контейнером для CameraImage - его можно заменить на метод, который вернёт Task:
static async Task<Image> CaptureImageAsync (… device, MethodChoiceEnum methodType) {
var tcsEventArgs = new TaskCompletionSource<NewFrameEventArgs>();
SomeEventHandler handler = (o, e) => tcsEventArgs.SetResult(e);
device.NewImage += handler;
var eventArgs = await tcsEventArgs;
device.NewImage -= handler;
switch (methodType) {
case MethodChoiceEnum.Sync:
return Device_NewFrame(eventArgs);
case MethodChoiceEnum.Parallel:
return Device_NewFrame_Parallel(eventArgs);
default:
throw …;
}
}
Для случая же обновляемого контейнера (который можно уже считать ViewModel) явно нужно событие обновления картинки:
private Image _cameraImage;
public Image CameraImage {
get => _cameraImage;
private set {
_cameraImage = value;
CameraImageChanged?.Invoke(this, EventArgs.Empty);
}
}
public event EventHandler CameraImageChanged;