Почему по умолчанию следует избегать использования делегатов в качестве методов и какие есть альтернативы:
Кривая обучения
Использование делегатов таким образом удивит многих людей.Не каждый может обернуться вокруг делегатов, или почему вы хотите поменять местами функции.Кажется, есть кривая обучения.Как только вы пройдете мимо этого, делегаты кажутся простыми.
Производительность и надежность
Потеря производительности при вызове делегатов таким образом.Это еще одна причина, по которой я бы по умолчанию использовал традиционное объявление метода, если в моем шаблоне не было включено что-то особенное.
Также существует проблема безопасности выполнения.Публичные поля могут быть обнуляемыми.Если вам передан экземпляр класса с открытым полем, вы должны проверить, что он не равен нулю, прежде чем использовать его.Это вредит перфекту и является отчасти хромым.
Вы можете обойти это, изменив все открытые поля на свойства (что в любом случае является правилом во всех стандартах кодирования .Net).Затем в установщик бросьте ArgumentNullException
, если кто-то попытается назначить null
.
Дизайн программы
Даже если вы можете справиться со всем этим, разрешить методыбыть изменчивым вообще противоречит многим проектам для статических ОО и функциональных языков программирования.
В статических ОО типы всегда статичны, а динамическое поведение допускается посредством полиморфизма.Вы можете узнать точное поведение типа на основе его типа во время выполнения.Это очень полезно при отладке существующей программы.Возможность изменения типов во время выполнения вредит этому.
В статических ОО и парадигмах функционального программирования ограничение и изоляция побочных эффектов весьма полезны , а использование полностью неизменнымструктура является одним из основных способов сделать это.Единственная точка раскрытия методов в качестве делегатов - это создание изменяемых структур, которые имеют совершенно противоположный эффект.
Альтернативы
Если вы действительно хотели пойти так далеко, чтобывсегда используйте делегаты для замены методов, вы должны использовать такой язык как IronPython или что-то еще, построенное поверх DLR.Эти языки будут доработаны и настроены для той парадигмы, которую вы пытаетесь реализовать.Пользователи и сопровождающие вашего кода не будут удивлены.
Тем не менее, существуют применения, которые оправдывают использование делегатов в качестве замены методов.Вы не должны рассматривать эту опцию, если у вас нет веских причин сделать это, чтобы преодолеть эти проблемы производительности, путаницы, надежности и дизайна.Это следует делать только в том случае, если вы получаете что-то взамен.
Использует
... Пример кода:
public class Context
{
private Func<int, int, int> executeStrategy;
public Context(Func<int, int, int> executeStrategy) {
this.executeStrategy = executeStrategy;
}
public int ExecuteStrategy(int a, int b) {
return executeStrategy(a, b);
}
}
- Я нашел конкретный случай, когдаЯ думаю, что публичные свойства делегатов гарантированы: для реализации Pattern Method Pattern с экземплярами вместо производных классов ...
... Это особенно полезно в автоматических интеграционных тестах, гдеу вас есть много настроек / сносить.В таких случаях часто имеет смысл сохранять состояние в классе, предназначенном для инкапсуляции шаблона, а не полагаться на прибор модульного теста.Таким образом, вы можете легко поддерживать совместное использование каркаса набора тестов между приборами, не полагаясь на (иногда дрянное) наследование тестовых приборов.Это также может быть более пригодным для распараллеливания, в зависимости от реализации ваших тестов.
var test = new MyFancyUITest
{
// I usually name these things in a more test specific manner...
Setup = () => { /* ... */ },
TearDown = () => { /* ... */ },
};
test.Execute();
Поддержка Intellisense
внешние пользователи функции видят безымянные параметры - иногда это полезнохотя было бы неплохо иметь возможность назвать их.
Используйте именованного делегата - я полагаю, что это даст вам хоть какой-то Intellisense для параметров (вероятно, только имена, менее вероятные документы XML - пожалуйста, исправьте меня, если я ошибаюсь):
public class MyClass
{
public delegate int DoSomethingImpl(int foo, int bizBar);
public DoSomethingImpl DoSomething = (x, y) => { return x + y; }
}