В C# я пытаюсь создать метод расширения для StringBuilder под названием AppendCollection (), который позволил бы мне сделать это:
var sb1 = new StringBuilder();
var sb2 = new StringBuilder();
var people = new List<Person>() { ...init people here... };
var orders = new List<Orders>() { ...init orders here... };
sb1.AppendCollection(people, p => p.ToString());
sb2.AppendCollection(orders, o => o.ToString());
string stringPeople = sb1.ToString();
string stringOrders = sb2.ToString();
stringPeople получал бы строчку для каждого человека в списке. Каждая строка будет результатом p.ToString (). То же самое и для stringOrders. Я не совсем уверен, как написать код, чтобы лямбды работали с дженериками.
Я хотел бы иметь возможность передавать лямбду в качестве средства форматирования, чтобы вы могли делать такие вещи, как sb1.AppendCollection (people, => p.FirstName + "" + p.LastName);





Что этот метод должен вернуть? Я вижу строку, но почему, если вы добавляете StringBuilder?
То, что вы пытаетесь сделать, довольно просто, но вам нужно точно объяснить, чего вы хотите.
Обновлять:
Вот мое мнение. Использование метода расширения для этого глупо и бессмысленно, если вы просто собираетесь передать новый StringBuilder и вернуть строку.
Обновление 2:
Теперь, когда я вижу такое использование, то, что вы делаете, - плохая практика. В идеале вы должны делать что-то вроде:
public static string Print<T>(this IEnumerable<T> col, Func<T,string> printer)
{
var sb = new StringBuilder();
foreach (T t in col)
{
sb.AppendLine(printer(t));
}
return sb.ToString();
}
string[] col = { "Foo" , "Bar" };
string lines = col.Print( s => s);
Обновление 3:
После дополнительных разъяснений:
public static void AppendCollection<T>(this StringBuilder sb,
List<T> col, Func<T,string> printer)
{
col.ForEach( o => sb.AppendLine(printer(o)));
}
(что то же самое, что сказал Бруно Конде)
И теперь он вам больше не нужен :)
Итак, меня проголосовали против, потому что я прошу разъяснений? Это дух ...
Я думаю, дело в том, что ваш пост - это не ответ, это вопрос. Я не голосовал против.
Вы можете избежать голосов против, попросив разъяснений в комментарии, а не в ответе. Я также не голосовал против. Это то, что я делаю, чтобы избегать людей, которые думают, что все, что не является ответом, бесполезно.
Но я этого не хотел. Я собирался опубликовать ответ, как только у меня будет разъяснение ...
Метод должен возвращать void, а не строку, как у меня вначале. Он должен работать как AppendLine (), но позволять передавать функцию сбора и форматирования.
Я хотел бы иметь метод расширения для StringBuilder, потому что есть другие строки, которые я хочу добавить в построитель до и после моей коллекции. Как вы думаете, почему это плохая практика?
Ну, вы не объяснили использование, поэтому у меня было так много начальных вопросов.
Для ясности, я изначально не голосовал против вас, хотя я могу полностью понять, почему некоторые люди могли проголосовать.
Используйте делегат Func<T,string>.
public static void AppendCollection<T>(this StringBuilder sb,
IEnumerable<T> collection, Func<T, string> method) {
foreach(T x in collection)
sb.AppendLine(method(x));
}
Мне это не нравится, поскольку это нарушает парадигму StringBuilder. Методы StringBuilder должны просто продолжать добавлять во внутренний буфер, пока ToString не будет вызван в построителе. Он сочетает в себе шаги добавления / добавления в строку и не похож на другие методы добавления в StringBuilder.
Конечно, я обновил ответ, чтобы упомянуть свое мнение по этому поводу, но это конкретно задано в вопросе.
Я полностью согласен. Я набрал этот пример кода слишком быстро. Я обновил вопрос.
Я не уверен, что вам нужно так много работать:
public static void AppendCollection( this StringBuilder builder,
ICollection collection )
{
foreach (var item in collection)
{
builder.AppendLine( Convert.ToString( item ) );
}
}
Используется как
List<Person> people = ...
StringBuilder builder = new StringBuilder();
builder.AppendCollection( people );
var s = builder.ToString();
Конечно, Person необходимо переопределить ToString (), чтобы получить правильный вывод для объекта Person.
Используя лямбда, вы можете форматировать элемент в коллекции, как хотите.
Конечно, но вы просто вызываете ToString ()
Наверное, мне следовало написать что-то вроде sb1.AppendCollection (p.FirstName + "" + p.LastName) в этом примере. Вот какая гибкость мне нравится в этой функции.
Лэнс, в этом случае я бы предпочел предоставить перегрузку для AppendCollection, которая позволяет вам указать форматирование, а затем написать собственный модуль форматирования для вашего типа. Таким образом, вы используете установленные интерфейсы и упрощаете повторное использование кода, вместо того, чтобы создавать совершенно новый API, нарушающий интерфейс.
Что-то типа:
public static void AppendCollection<TItem>(this StringBuilder builder, IEnumerable<TItem> items, Func<TItem, string> valueSelector)
{
foreach(TItem item in items)
{
builder.Append(valueSelector(item));
}
}
Я бы добавил полезное значение по умолчанию, чтобы сохранить указание лямбда в 90% случаев ...
public static void AppendCollection<TItem>(this StringBuilder builder, IEnumerable<TItem> items)
{
AppendCollection(builder, items, x=>x.ToString());
}
static class SBExtention
{
static string AppendCollection<T>(this StringBuilder sb,
IEnumerable<T> coll,
Func<T,string> action)
{
foreach(T t in coll)
{
sb.Append(action(t));
sb.Append("\n");
}
return sb.ToString();
}
}
Однако я думаю, вам будет лучше, если он вернет StringBuilder. Таким образом вы могли связать это:
static StringBuilder AppendCollection<T>(this StringBuilder sb,
IEnumerable<T> coll,
Func<T,string> action)
{
// same
return sb;
}
строка peopleAndOrders = sb.AppendCollection (люди, p => p.ToString ()) .AppendCollection (заказы, o => o.ToString ()). ToString ();
И я согласен с Дженнифер насчет случая по умолчанию:
public static StringBuilder AppendCollection<TItem>(
this StringBuilder builder,
IEnumerable<TItem> items)
{
return AppendCollection(builder, items, x=>x.ToString());
}
строка peopleAndOrders = sb.AppendCollection (люди) .AppendCollection (заказы) .ToString ();
Цепочка хороша, но я обычно предпочитаю, чтобы мои методы расширения работали так же, как и другие методы класса. Из-за изменения базовой схемы работы становится труднее понять.
public static void AppendCollection<T>(this StringBuilder builder, IEnumerable<T> list, Func<T,string> func)
{
foreach (var item in list)
{
builder.AppendLine(func(item));
}
}
Я бы не стал возвращать строку, я просто добавил бы ее в исходный Stringbuilder, который был передан.
Моя версия:
public static string AppendCollection<T>(this StringBuilder sb, IEnumerable<T> enumerable, Func<T, string> method)
{
List<T> l = new List<T>(enumerable);
l.ForEach(item => sb.AppendLine(method(item)));
return sb.ToString();
}
но в этом случае вы не должны возвращать строку. Я бы предпочел следующее:
public static void AppendCollection<T>(this StringBuilder sb, IEnumerable<T> enumerable, Func<T, string> method)
{
List<T> l = new List<T>(enumerable);
l.ForEach(item => sb.AppendLine(method(item)));
}
для использования как:
sb.AppendCollection(people, p => p.ToString());
sb.AppendCollection(orders, o => o.ToString());
Console.WriteLine(sb.ToString());
Я согласен с тем, что мне не следует возвращать строку. Я обновил вопрос.
По какой причине вы не хотите использовать для этого String.Join ()?