C# Метод, который принимает один элемент или список - PullRequest
1 голос
/ 16 марта 2020

У меня есть метод, который принимает один идентификатор прямо сейчас. Мне нужно добавить метод, который принимает список идентификаторов. Я могу выбрать один из методов для вызова другого, например, так:

void NotifyUsers(List<int> userIds) {//do things}

void NotifyUser(int userId)
{
  NotifyUsers(new List<int>{userId})
}

Или я могу изменить вызовы на NotifyUsers(new List<int>{9999}.

Или я могу использовать дженерики и т. Д. c.

В чем здесь «лучшая практика»?

Я понимаю, что это несколько основанные на мнении вопросы, но кажется, что вокруг этого должен быть стандарт, и я не могу его найти.

Ответы [ 2 ]

13 голосов
/ 16 марта 2020

Что такое «лучшая практика» здесь?

Лучшая практика заключается в применении подхода holisti c с особым акцентом на потребностях абонента . Это не обязательно означает предоставление максимальной гибкости для абонента! Это означает понимание вариантов использования для вызывающего абонента.

Способ, которым я лично разработал бы это решение:

// Notice: User, not Users. This notifies a single user.
void NotifyUser(int id) 
{
  // notify the user
}

void NotifyUsers(IEnumerable<int> ids)
{
  foreach(var id in ids) 
    NotifyUser(id);
}

Это подчеркивает для вызывающего абонента, что, если вы хотите сделать это один раз, вы используйте NotifyUser, и если у вас есть любая последовательность пользователей - не просто список - тогда вы вызываете NotifyUsers с этой последовательностью.

Теперь следующий вопрос, который я бы задал есть: хотят ли звонящие когда-либо делать это:

NotifyUsers(10, 20, 30);

В этом случае я бы добавил третью функцию:

void NotifyUsers(params int[] ids)
{
  NotifyUsers((IEnumerable<int>) ids);
}

Этот метод является гибким для звонящего, гарантируя, что большинство ваших методов тривиальны. Если в NotifyUser есть ошибка, вы хотите исправить ее только в одном месте.

Недостатком этого подхода является то, что NotifyUsers() становится легальным и не допускается. Кто-то может назвать это случайно и подумать, что он что-то делает. В этом случае вы можете заставить хотя бы одного:

void NotifyUsers(int id, params int[] ids)
{
  NotifyUser(id);
  NotifyUsers((IEnumerable<int>) ids);
}

Это иллюстрирует важный момент: размышление о потребностях звонящего также включает в себя психоанализ людей, которых вы не знаете, чтобы выяснить что они собираются делать неправильно, а затем предотвращать это, прежде чем это произойдет . Разработка API-интерфейсов, которые естественным образом приводят людей к только успеху, трудна!

(Также обратите внимание, что в этих набросках отсутствует обработка ошибок; возможно, вы хотите проверить, что последовательности и массивы не равны NULL, и и так далее.)

Ключ здесь: начать с понимания потребностей вызывающего абонента; разработать API, который отвечает их потребностям. Тогда осуществите это. Тем не менее, каким должен быть выбор реализации?

Это зависит от того, как работает уведомление последовательности пользователей. Решение, которое я набросал выше, делает некоторые предположения. Вот они:

  • не более эффективно уведомлять сотню пользователей "за один раз", чем уведомление сотен пользователей по одному за раз.
  • Если уведомление одного пользователя в последовательности завершается неудачно, правильное решение - перестать уведомлять.

Что если эти предположения неверны? Рассмотрим API, который обновляет базу данных, и дорогой частью является не обновление, а установление соединения с базой данных. В этом случае вы НЕ хотите писать:

void NotifyUser(int id) 
{
  Connect(); // Expensive
  Update(id);
  Disconnect();
}
void NotifyUsers(IEnumerable<int> ids)
{
  foreach(var id in ids)
    NotifyUser(id);
}

Потому что теперь вы делаете 100 подключений для 100 пользователей. Вместо этого вы хотите перевернуть скрипт:

void NotifyUser(int id) 
{
  NotifyUsers(Enumerable.Repeat(id, 1));
}
void NotifyUsers(IEnumerable<int> ids)
{
  Connect();
  foreach(var id in ids)
    Update(id);
  Disconnect();
}

Обратите внимание, что при переписывании реализации я не переписывал API; помните, что мы уже разработали API, чтобы он был хорошим для абонентов , поэтому, если этот API отвечает их потребностям, не меняйте его! Методы: абстракции ; мы можем изменить детали, чтобы соответствовать требованиям производительности. (И если мы не можем достичь поставленных целей из-за проблем с дизайном API, то мы не спроектировали API, который в первую очередь отвечал бы потребностям вызывающего абонента.)

А что теперь с обработкой ошибок? Существует множество возможностей:

  • Уведомления требуются для максимальной эффективности. Если одно уведомление в последовательности завершается сбоем, перехватите исключение, сожмите его и продолжайте работу с другими пользователями. Никогда не сообщайте вызывающему абоненту о сбое
  • Уведомления должны быть максимально эффективными, но вызывающие абоненты должны знать обо всех сбоях. Зарегистрируйте исключения и либо перебросьте их, либо верните отчет об ошибках вместо void.
  • Уведомления должны быть «все или ничего». То есть, если десятое уведомление терпит неудачу, то отменяет предыдущие уведомления, чтобы сохранить мир согласованным Это сложно . Вы не можете отменить звонок.

И так далее. Снова очень внимательно подумайте о потребностях звонящего . Что они ожидают, произойдет в случае неудачи? Вы знаете, как написать логи c, чтобы делать то, что ожидает вызывающий? Можете ли вы четко задокументировать это, чтобы звонящий знал, оправданы ли его ожидания?

5 голосов
/ 16 марта 2020

Если массив допустим вместо конкретно List<>, тогда вы можете использовать ключевое слово params . Например:

void NotifyUsers(params int[] userIds)
{
    //...
}

Который можно вызвать с нулевым или большим количеством аргументов:

NotifyUsers(123);
NotifyUsers(5, 67, 890);
// etc.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...