Comment puis-je trouver un élément spécifique dans une List <T>?


114

Mon application utilise une liste comme celle-ci:

List<MyClass> list = new List<MyClass>();

En utilisant la Addméthode, une autre instance de MyClassest ajoutée à la liste.

MyClass fournit, entre autres, les méthodes suivantes:

public void SetId(String Id);
public String GetId();

Comment puis-je trouver une instance spécifique de MyClassen utilisant la GetIdméthode? Je sais qu'il existe une Findméthode, mais je ne sais pas si cela fonctionnerait ici?!

Réponses:


263

Utiliser une expression lambda

MyClass result = list.Find(x => x.GetId() == "xy");

Remarque: C # a une syntaxe intégrée pour les propriétés. Au lieu d'écrire des méthodes getter et setter (comme vous pourriez être habitué à partir de Java), écrivez

private string _id;
public string Id
{
    get
    {
        return _id;
    }
    set
    {
        _id = value;
    }
}

valueest un mot clé contextuel connu uniquement dans l'accesseur set. Il représente la valeur attribuée à la propriété.

Étant donné que ce modèle est souvent utilisé, C # fournit des propriétés implémentées automatiquement . Il s'agit d'une version courte du code ci-dessus; cependant, la variable de sauvegarde est masquée et non accessible (elle est cependant accessible depuis la classe dans VB).

public string Id { get; set; }

Vous pouvez simplement utiliser les propriétés comme si vous accédiez à un champ:

var obj = new MyClass();
obj.Id = "xy";       // Calls the setter with "xy" assigned to the value parameter.
string id = obj.Id;  // Calls the getter.

En utilisant les propriétés, vous recherchez des éléments dans la liste comme ceci

MyClass result = list.Find(x => x.Id == "xy"); 

Vous pouvez également utiliser des propriétés implémentées automatiquement si vous avez besoin d'une propriété en lecture seule:

public string Id { get; private set; }

Cela vous permet de définir le Id dans la classe mais pas de l'extérieur. Si vous devez également le définir dans des classes dérivées, vous pouvez également protéger le setter

public string Id { get; protected set; }

Et enfin, vous pouvez déclarer les propriétés comme virtual et les remplacer dans les classes dérivées, ce qui vous permet de fournir différentes implémentations pour les getters et les setters; comme pour les méthodes virtuelles ordinaires.


Depuis C # 6.0 (Visual Studio 2015, Roslyn), vous pouvez écrire des propriétés automatiques en lecture uniquement avec un initialiseur en ligne

public string Id { get; } = "A07"; // Evaluated once when object is initialized.

Vous pouvez également initialiser les propriétés en lecture seule dans le constructeur à la place. Les propriétés automatiques accessibles uniquement sont lecture seule vraies lecture seule, contrairement aux propriétés implémentées automatiquement avec un setter privé.

Cela fonctionne également avec les propriétés automatiques en lecture-écriture:

public string Id { get; set; } = "A07";

À partir de C # 6.0, vous pouvez également écrire des propriétés en tant que membres à corps d'expression

public DateTime Yesterday => DateTime.Date.AddDays(-1); // Evaluated at each call.
// Instead of
public DateTime Yesterday { get { return DateTime.Date.AddDays(-1); } }

Voir: .NET Compiler Platform («Roslyn»)
          Nouvelles fonctionnalités du langage dans C # 6

À partir de C # 7.0 , les deux, getter et setter, peuvent être écrits avec des corps d'expression:

public string Name
{
    get => _name;                                // getter
    set => _name = value;                        // setter
}

Notez que dans ce cas, le setter doit être une expression. Cela ne peut pas être une déclaration. L'exemple ci-dessus fonctionne, car en C #, une affectation peut être utilisée comme expression ou comme instruction. La valeur d'une expression d'affectation est la valeur affectée où l'affectation elle-même est un effet secondaire. Cela vous permet d'affecter une valeur à plusieurs variables à la fois: x = y = z = 0équivaut à x = (y = (z = 0))et a le même effet que les instructions x = 0; y = 0; z = 0;.

La prochaine version du langage, C # 9.0, probablement disponible en novembre 2020, autorisera les propriétés en lecture seule (ou mieux initialiser une fois) que vous pourrez initialiser dans un initialiseur d'objet. Ceci n'est actuellement pas possible avec les propriétés de lecture uniquement.

public string Name { get; init; }

var c = new C { Name = "c-sharp" };

2
Excellente réponse, merci. Pour le fonctionnement de la base de données, cela ressemblerait à ceci: IQueryable<T> result = db.Set<T>().Find(//just id here//).ToList();Il saurait déjà que vous recherchez une clé primaire. Juste pour info.
Mr.Blond

Je sais que c'est une vieille réponse, mais je séparerais le get et le définirais en différentes méthodes afin que la valeur ne soit pas accidentellement définie lors d'une comparaison.
Joel Trauger

@JoelTrauger: Une comparaison lit la propriété et n'appelle donc que le getter.
Olivier Jacot-Descombes

C'est vrai, mais une affectation accidentelle appellera le setter et modifiera la propriété. Voir return object.property = valuevsreturn object.property == value
Joel Trauger

Un appel accidentel d'une méthode set distincte modifiera également la propriété. Je ne vois pas comment des méthodes de réglage séparées peuvent améliorer la sécurité.
Olivier Jacot-Descombes

19
var list = new List<MyClass>();
var item = list.Find( x => x.GetId() == "TARGET_ID" );

ou s'il n'y en a qu'un et que vous voulez faire en sorte que quelque chose comme SingleOrDefaultpeut être ce que vous voulez

var item = list.SingleOrDefault( x => x.GetId() == "TARGET" );

if ( item == null )
    throw new Exception();

Pourquoi allez-vous utiliser singleOrDefault si vous voulez lever une exception, utilisez Single ()
Nom de code Jack


6

Ou si vous ne préférez pas utiliser LINQ, vous pouvez le faire à l'ancienne:

List<MyClass> list = new List<MyClass>();
foreach (MyClass element in list)
{
    if (element.GetId() == "heres_where_you_put_what_you_are_looking_for")
    {

        break; // If you only want to find the first instance a break here would be best for your application
    }
}

4

Vous pouvez également utiliser les extensions LINQ :

string id = "hello";
MyClass result = list.Where(m => m.GetId() == id).First();

4
ou l'autre surcharge de First:MyClass result = list.First(m => m.GetId() == id);
Marcel Gosselin

3

Vous pouvez résoudre votre problème de manière plus concise avec un prédicat écrit à l'aide de la syntaxe de méthode anonyme:

MyClass found = list.Find(item => item.GetID() == ID);

0
public List<DealsCategory> DealCategory { get; set; }
int categoryid = Convert.ToInt16(dealsModel.DealCategory.Select(x => x.Id));

Bien que ce code puisse répondre à la question, il est préférable d'expliquer comment résoudre le problème et de fournir le code à titre d'exemple ou de référence. Les réponses basées uniquement sur le code peuvent prêter à confusion et manquer de contexte.
Robert Columbia

0

Vous pouvez créer une variable de recherche pour contenir vos critères de recherche. Voici un exemple utilisant la base de données.

 var query = from o in this.mJDBDataset.Products 
             where o.ProductStatus == textBox1.Text || o.Karrot == textBox1.Text 
             || o.ProductDetails == textBox1.Text || o.DepositDate == textBox1.Text 
             || o.SellDate == textBox1.Text
             select o;

 dataGridView1.DataSource = query.ToList();

 //Search and Calculate
 search = textBox1.Text;
 cnn.Open();
 string query1 = string.Format("select * from Products where ProductStatus='"
               + search +"'");
 SqlDataAdapter da = new SqlDataAdapter(query1, cnn);
 DataSet ds = new DataSet();
 da.Fill(ds, "Products");
 SqlDataReader reader;
 reader = new SqlCommand(query1, cnn).ExecuteReader();

 List<double> DuePayment = new List<double>();

 if (reader.HasRows)
 {

  while (reader.Read())
  {

   foreach (DataRow row in ds.Tables["Products"].Rows)
   {

     DuePaymentstring.Add(row["DuePayment"].ToString());
     DuePayment = DuePaymentstring.Select(x => double.Parse(x)).ToList();

   }
  }

  tdp = 0;
  tdp = DuePayment.Sum();                        
  DuePaymentstring.Remove(Convert.ToString(DuePaymentstring.Count));
  DuePayment.Clear();
 }
 cnn.Close();
 label3.Text = Convert.ToString(tdp + " Due Payment Count: " + 
 DuePayment.Count + " Due Payment string Count: " + DuePaymentstring.Count);
 tdp = 0;
 //DuePaymentstring.RemoveRange(0,DuePaymentstring.Count);
 //DuePayment.RemoveRange(0, DuePayment.Count);
 //Search and Calculate

Ici, "var query" génère les critères de recherche que vous donnez via la variable de recherche. Ensuite, "DuePaymentstring.Select" sélectionne les données correspondant à vos critères donnés. N'hésitez pas à demander si vous avez des problèmes de compréhension.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.