Как стать автором
Обновить

Комментарии 22

Для одинаковых разделителей string.Join подойдет
Join с уже готовой коллекцией работает. То есть сначала надо создать коллекцию, и только потом вызвать Join.
В чем сложность? Дисклаймер, мне тот код который щас напишу не нравится =)
var l = new List<string>();
            if (condition1) l.Add("CONDITION1 = ? ");
            if (condition2) l.Add("CONDITION2 = ? ");
            if (condition3) l.Add("CONDITION3 = ? ");
string sql = "SELECT * FROM table_1 WHERE " + string.Join(" AND ", l.ToArray());
Спасибо. Я люблю скоролить (с)

Есть же тег «source lang=cs» используйте его.
Спасибо! О таком теге раньше не знал, пользовался по-старинке сторонней утилитой.
1) Не уверен, так ли это в C#, но почти наверняка конкатенация — это лишний вызов конструктора (как в Java и JS).
2) Конкатенация в Append (см //первая порция и //последующие порции) сделана плохо. В результате каждый раз в цикле будет вычисляться этот дурацкий if (), который имеет смысл только для первого прохода.
Почему не бы сделать по такой схеме (псевдокод):
content = elem[0];
while not end {
content += ', ' + elem[nextNdx]
}
1) В C# аналогично. Нужно использовать StringBuilder для таких алгоритмов.
1) Не уверен, так ли это в C#, но почти наверняка конкатенация — это лишний вызов конструктора (как в Java и JS).

Так и будет. Но это не то место, где можно получить большую экономию в скорости/памяти. Поэтому я и предпочел использовать более читаемый вариант.

2) Цикл в примере ниже написан только для краткости самого примера. Я имел ввиду ситуацию, когда каждое условие проверяется отдельно. Поэтому Append может и не вызваться ни разу. Или всего для 1,2 условий из, скажем, десятка.

Естественно, когда у нас сразу есть коллекция, подобные костыли не нужны и задача решается гораздо проще.
Мне кажется, вы путаете читаемость с примитивизмом.
Вот тут я поспорю: что легче воспринимается?

StringBuilder sb = new StringBuilder("1");
sb.Append("2");
string s = sb.ToString();

или

string s = "1" + "2";


И потом: простое != примитивное. Если уж на то пошло, вот такая строка (из комментария ниже) не примитивна, но и не читаема:

new [] { } .Where(p => p != null).Aggregate((f, s) => f + ', ' + s);
Если через запятую:

new [] { } .Where(p => p != null).Aggregate((f, s) => f + ', ' + s);

Если нужно с последним «и», то будет чуть сложнее, но тоже не в 50 строчек.
Еще раз повторюсь:

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

На самом деле речь идет о том, что у нас есть ряд независимых условий, каждое из которых проверяется отдельно. И только если это условие истинно, необходимо добавить в строку очередной фрагмент. А если работать с готовой коллекцией, то, разумеется, проще (как в первом комментарии):

s = string.Join(", ", arr);
var filters = new[]{
                           new FuncTuple(() => true, "sdf"),
                           new FuncTuple(() => false, "sdfdfgdfg"),
                           new FuncTuple(() => true, "sddfgdfgf")
                           };

var s = string.Format("dfsdfsdfsd {0}", string.Join(", ", from x in filters where x.Item1() select x.Item2));
Знание лямбда-функций и Linq НЕ означает, что их надо использовать везде, где только можно =)
Конечно не означает. Но в данном случае, на моей взгляд, это уместней, чем вводить новые сущности, типа своего билдера. Кода у вас больше, работает он медленней из-за использования конкатенации и кастомной логики в нем больше, а значит больше вероятность ошибки. Опять же по хорошему на все ваши if'ы надо юниттесты писать. К тому же реализация вроде приведенного FilterList может использоваться, если понадобиться составлять список из числе или структур или модельных классов, каждому из которых соответствует определенный предикат.
Интересно было бы сравнить по скорости/памяти ваш и мой подходы.

Хотя заметный выигрыш в такой постановке задачи сложно получить: смысл увеличивать скорость функции, которая вызывается раз в несколько минут (ну пусть, в несколько секунд)?
Согласен, в данном случае скоростью можно пренебречь. Но остаются другие приведенные отличия.
Для любителей ООП

class FilterList<TResult>:IEnumerable<TResult>
{
	private readonly List<Tuple<Func<bool>,TResult>> _filters = new List<Tuple<Func<bool>,TResult>>();

	public void Add(Func<bool> predicate, TResult value)
	{
	   _filters.Add(Tuple.Create(predicate, value));
	}

	public IEnumerator<TResult> GetEnumerator()
	{
		return (from x in _filters where x.Item1() select x.Item2).GetEnumerator();
	}

	IEnumerator IEnumerable.GetEnumerator()
	{
		return GetEnumerator();
	}
}

var t = new FilterList<string>
			        {
			        	{()=>true, "fdgdgfg"},
					{()=>false, "fdgsdfsdfdgfg"},
					{()=>true, "fdgfhdhfdhgdgfg"},
			        };
var s = string.Join(", ", t);
Если уж делать класс ListBuilder, то хотя бы сделать красиво.
1. Использовать StringBuilder
2. Флаг first — убрать нафиг, сделать просто проверку StringBuilder на null и создавать при добавлении первой непустой строки (так мы его не будем создавать без необходимости).

А еще проще и эффективнее — сделать расширение для StringBuilder'а. И сделать добавление как в первом варианте. Просто и эффективно.

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;
}


Ай-я-я-яай, кто же SQL собирает конкатенацией…
Во первых, это все можно реализовать с помощью, вами не любимого LINQ, хотя кроме CURSORов ORACLE, пожалуй. Но ведь то — совсем другой уровень…
И вот ведь вопрос, зачем здесь «WHERE 1=1», простите???
Это что бы можно было не заморачиваться с AND — тупо добавлять строчку и все.
А разве этот топик не посвящен сложным типам конкатенаций, чтобы они формировали правильные запросы, к примеру, без лишних операций в нагрузку баз данных?
Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории