10 votes

c# - LINQ select from Collection

J'essaie d'écrire un simple Select sur une classe qui hérite de IList .

public class RowDataCollection : IList<RowData> {
  private List<RowData> rowList;

  internal RowDataCollection(List<RowData> data) {
    rowList = data;
  }
  // ...
}

public RowDataCollection Rows;

public RowDataCollection Select(string colName, object value) {
  List<RowData> rowList = from item in Rows
         where item[colName].Value == value
         select item;
  return new RowDataCollection(rowList);
}

Quelques problèmes que j'ai :

D'abord :

  • Rapports VS2010 Cannot implicitly convert type 'IEnumerable<RowData>' to 'List<RowData>'. An explicit conversion exists (are you missing a cast?)

OK, où va le CAST ?

Deuxièmement :

  • Quelqu'un pourrait faire passer un message non valide colName (c'est-à-dire String.IsNullOrEmpty(colName) ) ou un paramètre nul (object value == null) .

Comment gérer le retour de ma fonction si les paramètres d'entrée ne sont pas valides ?

[Résolu]

J'ai édité mon Select (je l'ai même renommé selon les suggestions faites ici). J'ai dû utiliser un commutateur pour convertir le type de données dans lequel se trouvaient les données, mais cela fonctionne.

public RowDataCollection SelectRow(string colName, object value) {
  if (!String.IsNullOrEmpty(colName) && (value != null) && (0 < Rows.Count)) {
    switch (Rows[0][colName].GetValueType()) {
      case TableDataType.Boolean:
        return new RowDataCollection(Rows.Where(r => (bool)r[colName].Value == (bool)value).ToList());
      case TableDataType.Character:
        return new RowDataCollection(Rows.Where(r => (char)r[colName].Value == (char)value).ToList());
      case TableDataType.DateTime:
        return new RowDataCollection(Rows.Where(r => (DateTime)r[colName].Value == (DateTime)value).ToList());
      case TableDataType.Decimal:
        return new RowDataCollection(Rows.Where(r => (Decimal)r[colName].Value == (Decimal)value).ToList());
      case TableDataType.Integer:
        return new RowDataCollection(Rows.Where(r => (int)r[colName].Value == (int)value).ToList());
      case TableDataType.String:
        return new RowDataCollection(Rows.Where(r => r[colName].Value.ToString() == value.ToString()).ToList());
    }
  }
  return null;
}

[Résolu (version courte)]

Jon Skeet a posté ceci à peu près en même temps que ma solution, et (comme toujours) son code est bien plus beau.

public RowDataCollection SelectRow(string colName, object value) {
  List<RowData> rowList = Rows.Where(r => r[colName].Value.Equals(value)).ToList();
  return new RowDataCollection(rowList);
}

@Jon Skeet : Si jamais je vois votre visage dans la même file d'attente pour un poste de développeur de logiciels auquel je postule, je vais faire demi-tour et rentrer chez moi.

Tout le monde : Merci pour votre aide !

14voto

Jon Skeet Points 692016

Le résultat d'une requête comme celle-ci n'est pas une List<T> c'est une IEnumerable<T> . Si vous voulez convertir cela en un List<T> Il suffit d'appeler ToList :

List<RowData> rowList = (from item in Rows
                         where item[colName].Value == value
                         select item).ToList();

En fait, vous appelez seulement Where dans votre requête. Je réécrirais ça comme :

List<RowData> rowList = Rows.Where(item => item[colName].Value.Equals(value))
                            .ToList();

Je renommerais également la méthode en quelque chose qui est évidemment filtrage plutôt que projection de étant donné que ce dernier est l'utilisation la plus courante du terme "select" dans LINQ.

En ce qui concerne les paramètres d'entrée, je vous suggère de valider les arguments et de lever une exception s'ils ne sont pas valides :

if (string.IsNullOrEmpty(colName))
{
    throw new ArgumentException("colName");
}

1voto

user7116 Points 39829

Vous ne pouvez pas directement lancer un IEnumerable<RowData> a un List<RowData> Cependant, il existe une fonction de commodité Enumerable.ToList<T>() utilisé comme ça :

List<RowData> rowList = (from item in Rows
                         where item[colName].Value == value
                         select item).ToList();

Pour ce qui est de votre deuxième question, une exception se produirait au cours de l'exécution du ToList() car l'expression LINQ est évaluée immédiatement. Vous disposez de quelques options, dont celle de lancer ArgumentException ou de retourner une liste vide. Cela dépend de vos cas d'utilisation. Je suggérerais de simplement lancer une exception (en supposant que vous ayez un certain HasColumn() sur votre RowData classe) :

if (colName == null)
{
    throw new ArgumentNullException("colName");
}
else if (!Rows.All(row => row.HasColumn(colName)))
{
    throw new ArgumentException("No such column " + colName, "colName");
}

Selon votre édition, une autre approche, si une colonne manquante n'est pas nécessairement un "problème" :

...
// note the change to Any()
else if (!Rows.Any(row => row.HasColumn(colName))
{
    throw new ArgumentException("No such column " + colName, "colName");
}

List<RowData> rowList = (from item in Rows
                         where item.HasColumn(colName)
                            && item[colName].Value == value
                         select item).ToList();

1voto

Justin Niessner Points 144953

Vous obtenez le message d'erreur parce que les requêtes LINQ renvoient IEnumerable, et non List.

Si vous avez besoin d'une liste, c'est assez facile :

List<RowData> rowList = (from item in Rows
                         where item[colName].Value == value
                         select item).ToList();

-1voto

alexanderb Points 6553

Vous devez convertir IQueriable<> en List<>, en appelant ToList() ;

  public RowDataCollection Select(string colName, object value) {
      List<RowData> rowList = from item in Rows
             where item[colName].Value == value
             select item;
      return new RowDataCollection(rowList.ToList());
    }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X