Многим, наверно, знакома задачка, которую часто решают на скорую руку: добавить в строку фрагмент, если выполнено очередное условие.
Типичный кусок кода (немного надуманный пример, но все же):
На выходе, если все аргументы нулевые, получим строку "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.