Search
Write a publication
Pull to refresh

Миникостыли: склеиваем строку из фрагментов

Reading time3 min
Views805
Многим, наверно, знакома задачка, которую часто решают на скорую руку: добавить в строку фрагмент, если выполнено очередное условие.

Типичный кусок кода (немного надуманный пример, но все же):
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.
Tags:
Hubs:
Total votes 22: ↑4 and ↓18-14
Comments22

Articles