Minicut: glue a string of fragments
Probably many people are familiar with the problem, which is often solved in haste: add a fragment to the line if the next condition is fulfilled.
A typical piece of code (a bit far-fetched example, but still):
At the output, if all arguments are zero, we get the string "ARG1 ARG2 ARG3".
And if we want "ARG1 , ARG2 , ARG3"? Or even "ARG1 , ARG2 and ARG3"?
In this case, very often (if you do not use a special separator before the last word), use the following solution:
There is still a slight variation of the same method, when an “extra” argument is added to the beginning of the line and now the separator is written BEFORE the next fragment. Plus the fact that you don’t have to cut anything off the end of the line. The following is one example where this approach is used:
And now, I want to show what crutches I use myself:
1) Extension
Suppose that all delimiters should be the same and the start line should be empty. Then it will be convenient to write an extension function for the string:
2) Helper class
In order to use a special separator before the last fragment, the extension is no longer necessary and you have to use a helper class:
Usage example:
And another example:
I hope this seems useful to someone, or at least interesting (at least for discussion).
ps Yes, I know about the existence of StringBuilder and IsNullOrWhiteSpace. But I won’t get much from StringBuilder, so I decided not to complicate the code. And IsNullOrWhiteSpace is available from the 4th version of the framework, and crutches themselves were invented for 3.5.
A typical piece of code (a bit far-fetched example, but still):
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);
//...
}
At the output, if all arguments are zero, we get the string "ARG1 ARG2 ARG3".
And if we want "ARG1 , ARG2 , ARG3"? Or even "ARG1 , ARG2 and ARG3"?
In this case, very often (if you do not use a special separator before the last word), use the following solution:
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);
//...
}
In other words, they put a separator after each word, and then “bite” the extra separator at the end of the line. There is still a slight variation of the same method, when an “extra” argument is added to the beginning of the line and now the separator is written BEFORE the next fragment. Plus the fact that you don’t have to cut anything off the end of the line. The following is one example where this approach is used:
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;
}
I will not consider options when all possible combinations of conditions are sorted out. And now, I want to show what crutches I use myself:
1) Extension
Suppose that all delimiters should be the same and the start line should be empty. Then it will be convenient to write an extension function for the string:
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) Helper class
In order to use a special separator before the last fragment, the extension is no longer necessary and you have to use a helper class:
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;
}
}
Usage example:
ListBuilder lb = new ListBuilder("Вы нашли ", ", ", " и ");
foreach (string s in new string[] { "клюшку", "ложку", "поварешку", "черпак" })
lb.Append(s); //соль тут
Console.WriteLine(lb + "!");
And another example:
ListBuilder lb = new ListBuilder("Извините, произошла ошибка, так как ", ", ", " и вообще ");
if (!IsAutorized()) lb.Append("не удалось авторизоваться");
if (!IsAdmin()) lb.Append("вы не админ");
if (!IsBadDay()) lb.Append("сегодня плохой день");
if (!IsAutorized() || !IsAdmin() || !IsBadDay())
Console.WriteLine(lb + ".");
I hope this seems useful to someone, or at least interesting (at least for discussion).
ps Yes, I know about the existence of StringBuilder and IsNullOrWhiteSpace. But I won’t get much from StringBuilder, so I decided not to complicate the code. And IsNullOrWhiteSpace is available from the 4th version of the framework, and crutches themselves were invented for 3.5.