Dans de nombreux endroits de notre code, nous avons des collections d'objets, à partir desquelles nous devons créer une liste séparée par des virgules. Le type de collection varie: il peut s'agir d'un DataTable pour lequel nous avons besoin d'une certaine colonne, ou d'une liste <Client>, etc.
Maintenant, nous parcourons la collection et utilisons la concaténation de chaînes, par exemple:
string text = "";
string separator = "";
foreach (DataRow row in table.Rows)
{
text += separator + row["title"];
separator = ", ";
}
Y at-il un meilleur modèle pour cela? Idéalement, j'aimerais une approche que nous pourrions réutiliser en envoyant simplement une fonction pour obtenir le bon champ/propriété/colonne de chaque objet.
// using System.Collections;
// using System.Collections.Generic;
// using System.Linq
public delegate string Indexer<T>(T obj);
public static string concatenate<T>(IEnumerable<T> collection, Indexer<T> indexer, char separator)
{
StringBuilder sb = new StringBuilder();
foreach (T t in collection) sb.Append(indexer(t)).Append(separator);
return sb.Remove(sb.Length - 1, 1).ToString();
}
// version for non-generic collections
public static string concatenate<T>(IEnumerable collection, Indexer<T> indexer, char separator)
{
StringBuilder sb = new StringBuilder();
foreach (object t in collection) sb.Append(indexer((T)t)).Append(separator);
return sb.Remove(sb.Length - 1, 1).ToString();
}
// example 1: simple int list
string getAllInts(IEnumerable<int> listOfInts)
{
return concatenate<int>(listOfInts, Convert.ToString, ',');
}
// example 2: DataTable.Rows
string getTitle(DataRow row) { return row["title"].ToString(); }
string getAllTitles(DataTable table)
{
return concatenate<DataRow>(table.Rows, getTitle, '\n');
}
// example 3: DataTable.Rows without Indexer function
string getAllTitles(DataTable table)
{
return concatenate<DataRow>(table.Rows, r => r["title"].ToString(), '\n');
}
string.Join(", ", Array.ConvertAll(somelist.ToArray(), i => i.ToString()))
static string ToCsv<T>(IEnumerable<T> things, Func<T, string> toStringMethod)
{
StringBuilder sb = new StringBuilder();
foreach (T thing in things)
sb.Append(toStringMethod(thing)).Append(',');
return sb.ToString(0, sb.Length - 1); //remove trailing ,
}
Utilisez comme ceci:
DataTable dt = ...; //datatable with some data
Console.WriteLine(ToCsv(dt.Rows, row => row["ColName"]));
ou:
List<Customer> customers = ...; //assume Customer has a Name property
Console.WriteLine(ToCsv(customers, c => c.Name));
Je n'ai pas de compilateur à portée de main, mais en théorie cela devrait fonctionner. Et comme chacun le sait, en théorie, la pratique et la théorie sont les mêmes. En pratique, ils ne le sont pas.
J'ai trouvé string.Join and Lambda Select> aide à écrire le code minimum.
List<string> fruits = new List<string>();
fruits.Add("Mango");
fruits.Add("Banana");
fruits.Add("Papaya");
string commaSepFruits = string.Join(",", fruits.Select(f => "'" + f + "'"));
Console.WriteLine(commaSepFruits);
List<int> ids = new List<int>();
ids.Add(1001);
ids.Add(1002);
ids.Add(1003);
string commaSepIds = string.Join(",", ids);
Console.WriteLine(commaSepIds);
List<Customer> customers = new List<Customer>();
customers.Add(new Customer { Id = 10001, Name = "John" });
customers.Add(new Customer { Id = 10002, Name = "Robert" });
customers.Add(new Customer { Id = 10002, Name = "Ryan" });
string commaSepCustIds = string.Join(", ", customers.Select(cust => cust.Id));
string commaSepCustNames = string.Join(", ", customers.Select(cust => "'" + cust.Name + "'"));
Console.WriteLine(commaSepCustIds);
Console.WriteLine(commaSepCustNames);
Console.ReadLine();
Vous pouvez écrire une fonction qui transforme un IEnumerable en une chaîne séparée par des virgules
public string Concat(IEnumerable<string> stringList)
{
StringBuilder textBuilder = new StringBuilder();
string separator = String.Empty;
foreach(string item in stringList)
{
textBuilder.Append(separator);
textBuilder.Append(item);
separator = ", ";
}
return textBuilder.ToString();
}
Vous pouvez ensuite utiliser Linq pour interroger votre collection/dataset/etc afin de fournir la chaîne de caractères.
Dans .NET 4, vous pouvez simplement faire string.Join(", ", table.Rows.Select(r => r["title"]))
En passant: la première modification que je ferais consiste à utiliser la classe StringBuilder au lieu d'une chaîne - cela économisera des ressources.
J'adore Matt Howells réponse dans cet article:
Je devais en faire une extension:
public static string ToCsv<T>(this IEnumerable<T> things, Func<T, string> toStringMethod)
Utilisation: [Je reçois tous les emails et les transforme en chaîne csv pour les emails]:
var list = Session.Find("from User u where u.IsActive = true").Cast<User>();
return list.ToCsv(i => i.Email);
string strTest = "1,2,4,6";
string[] Nums = strTest.Split(',');
Console.Write(Nums.Aggregate<string>((first, second) => first + "," + second));
//OUTPUT:
//1,2,4,6
Pour les collections, vous pouvez également utiliser cette méthode, par exemple:
string.Join(", ", contactsCollection.Select(i => i.FirstName);
Vous pouvez sélectionner n'importe quelle propriété que vous souhaitez séparer.
Voici ma réponse préférée adaptée à la question, .__ et corrigée Convert to ConvertAll:
string text = string.Join(", ", Array.ConvertAll(table.Rows.ToArray(), i => i["title"]));