Многим, наверно, знакома задачка, которую часто решают на скорую руку: добавить в строку фрагмент, ��сли выполнено очередное условие.
Типичный кусок кода (немного надуманный пример, но все же):
На выходе, если все аргументы нулевые, получим строку "ARG1 ARG2 ARG3 ".
А если мы захотим "ARG1, ARG2, ARG3"? Или даже "ARG1, ARG2 and ARG3"?
В таком случае, очень часто (если не использовать особый разделитель перед последним словом) используют следующее решение:
Есть еще небольшая вариация того же метода, когда добавляется «лишний» аргумент в начало строки и теперь разделитель пишется ПЕРЕД очередным фрагментом. Плюс тот, что не надо ничего отрезать от конца строки. Ниже приведен один из примеров, когда используется такой подход:
А теперь, я хочу показать какие костыли использую сам:
1) Расширение
Предположим, что все разделители должны быть одинаковые и начальная строка должна быть пустой. Тогда удобно будет написать функцию-расширение для строки:
2) Вспомогательный класс
Для того, чтобы использовать специальный разделитель перед последним фрагментом, расширением уже не обойтись и придется использовать вспомогательный класс:
Пример использования:
И еще пример:
Надеюсь, кому-то это покажется полезным или, по крайней мере, интересным (хотя бы для обсуждения).
p.s. Да, я знаю о существовании StringBuilder и IsNullOrWhiteSpace. Но большого выигрыша от StringBuilder тут особо не получить, поэтому код я решил не усложнять. А IsNullOrWhiteSpace доступен с 4й версии фреймворка, а сами костыли были придуманы еще для 3.5.
Типичный кусок кода (немного надуманный пример, но все же):
void SomeFunc(string arg1, string arg2, string arg3) { string msg = ""; if (arg1 == null) msg += "ARG1 "; if (arg2 == null) msg += "ARG2 "; if (arg3 == null) msg += "ARG3 "; if (msg != "") throw new ArgumentNullException(msg); //... }
На выходе, если все аргументы нулевые, получим строку "ARG1 ARG2 ARG3 ".
А если мы захотим "ARG1, ARG2, ARG3"? Или даже "ARG1, ARG2 and ARG3"?
В таком случае, очень часто (если не использовать особый разделитель перед последним словом) используют следующее решение:
Иными словами, ставят разделитель после каждого слова, а потом «откусывают» лишний разделитель в конце строки.void SomeFunc(string arg1, string arg2, string arg3) { string msg = ""; if (arg1 == null) msg += "ARG1, "; if (arg2 == null) msg += "ARG2, "; if (arg3 == null) msg += "ARG3, "; if (msg.EndsWith(", ")) msg = msg.Substring(0, msg.Length - 2); if (msg != "") throw new ArgumentNullException(msg); //... }
Есть еще небольшая вариация того же метода, когда добавляется «лишний» аргумент в начало строки и теперь разделитель пишется ПЕРЕД очередным фрагментом. Плюс тот, что не надо ничего отрезать от конца строки. Ниже приведен один из примеров, когда используется такой подход:
Варианты, когда перебираются всевозможные комбинации условий, я, по очевидным причинам, рассматривать не буду.string BuildSQL(string arg1, string arg2, string arg3) { string sql = "SELECT * FROM table_1 WHERE 1=1 "; if (arg1 != null) sql += "AND ARG1 = ? "; if (arg2 != null) sql += "AND ARG1 = ? "; if (arg3 != null) sql += "AND ARG1 = ? "; return sql; }
А теперь, я хочу показать какие костыли использую сам:
1) Расширение
Предположим, что все разделители должны быть одинаковые и начальная строка должна быть пустой. Тогда удобно будет написать функцию-расширение для строки:
public static class StringEx { public static string Append(this string main, string str, string delimiter) { if (string.IsNullOrEmpty(str) || string.IsNullOrEmpty(delimiter)) return main; else if (string.IsNullOrEmpty(main) || main.Trim() == "" || str.Trim() == "" || delimiter.Trim() == "") return str; else return main + delimiter + str; } }
2) Вспомогательный класс
Для того, чтобы использовать специальный разделитель перед последним фрагментом, расширением уже не обойтись и придется использовать вспомогательный класс:
class ListBuilder { bool first = true; string last = ""; string content = ""; string delimiter = ", "; string lastdelimiter = " and "; public ListBuilder() { } public ListBuilder(string delimiter, string lastdelimiter) { this.delimiter = delimiter ?? ", "; this.lastdelimiter = lastdelimiter ?? " and "; } public ListBuilder(string content, string delimiter, string lastdelimiter) { this.content = content ?? ""; this.delimiter = delimiter ?? ", "; this.lastdelimiter = lastdelimiter ?? " and "; } public void Append(string str) { if (str == null || str.Trim() == "") return; //первая порция if (first) { content += str; first = false; } //последующие порции else { if (last != "") content += delimiter + last; last = str; } } public override string ToString() { if (last != "") return content + lastdelimiter + last; else return content; } }
Пример использования:
ListBuilder lb = new ListBuilder("Вы нашли ", ", ", " и "); foreach (string s in new string[] { "клюшку", "ложку", "поварешку", "черпак" }) lb.Append(s); //��оль тут Console.WriteLine(lb + "!");
И еще пример:
ListBuilder lb = new ListBuilder("Извините, произошла ошибка, так как ", ", ", " и вообще "); if (!IsAutorized()) lb.Append("не удалось авторизоваться"); if (!IsAdmin()) lb.Append("вы не админ"); if (!IsBadDay()) lb.Append("сегодня плохой день"); if (!IsAutorized() || !IsAdmin() || !IsBadDay()) Console.WriteLine(lb + ".");
Надеюсь, кому-то это покажется полезным или, по крайней мере, интересным (хотя бы для обсуждения).
p.s. Да, я знаю о существовании StringBuilder и IsNullOrWhiteSpace. Но большого выигрыша от StringBuilder тут особо не получить, поэтому код я решил не усложнять. А IsNullOrWhiteSpace доступен с 4й версии фреймворка, а сами костыли были придуманы еще для 3.5.
