Come posso trovare un elemento specifico in un List <T>?


114

La mia applicazione utilizza un elenco come questo:

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

Utilizzando il Addmetodo, un'altra istanza di MyClassviene aggiunta all'elenco.

MyClass fornisce, tra gli altri, i seguenti metodi:

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

Come posso trovare un'istanza specifica MyClassdi utilizzando il GetIdmetodo? So che esiste il Findmetodo, ma non so se funzionerebbe qui ?!

Risposte:


263

Usa un'espressione lambda

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

Nota: C # ha una sintassi incorporata per le proprietà. Invece di scrivere metodi getter e setter (come potresti essere abituato da Java), scrivi

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

valueè una parola chiave contestuale nota solo nella funzione di accesso set. Rappresenta il valore assegnato alla proprietà.

Poiché questo modello viene spesso utilizzato, C # fornisce proprietà implementate automaticamente . Sono una versione breve del codice sopra; tuttavia, la variabile di supporto è nascosta e non accessibile (è comunque accessibile dall'interno della classe in VB).

public string Id { get; set; }

Puoi semplicemente utilizzare le proprietà come se stessi accedendo a un campo:

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

Utilizzando le proprietà, cercheresti gli elementi nell'elenco come questo

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

Puoi anche utilizzare proprietà implementate automaticamente se hai bisogno di una proprietà di sola lettura:

public string Id { get; private set; }

Ciò ti consente di impostare Idla classe all'interno ma non dall'esterno. Se è necessario impostarlo anche in classi derivate, è anche possibile proteggere il setter

public string Id { get; protected set; }

Infine, è possibile dichiarare le proprietà come virtuale sovrascriverle nelle classi derivate, consentendo di fornire diverse implementazioni per getter e setter; proprio come per i metodi virtuali ordinari.


A partire da C # 6.0 (Visual Studio 2015, Roslyn) è possibile scrivere proprietà automatiche solo getter con un inizializzatore inline

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

È inoltre possibile inizializzare le proprietà solo getter all'interno del costruttore. Getter sola auto-proprietà sono vere proprietà di sola lettura, a differenza di proprietà auto-implementato con un setter privato.

Funziona anche con le proprietà automatiche di lettura-scrittura:

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

A partire da C # 6.0 è anche possibile scrivere proprietà come membri con corpo di espressione

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

Vedere: Nuove funzionalità per il linguaggio di .NET Compiler Platform ("Roslyn")
         in C # 6

A partire da C # 7.0 , entrambi, getter e setter, possono essere scritti con corpi di espressione:

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

Nota che in questo caso il setter deve essere un'espressione. Non può essere un'affermazione. L'esempio sopra funziona, perché in C # un'assegnazione può essere usata come espressione o come istruzione. Il valore di un'espressione di assegnazione è il valore assegnato in cui l'assegnazione stessa è un effetto collaterale. Ciò consente di assegnare un valore a più di una variabile contemporaneamente: x = y = z = 0è equivalente x = (y = (z = 0))e ha lo stesso effetto delle istruzioni x = 0; y = 0; z = 0;.

La prossima versione del linguaggio, C # 9.0, probabilmente disponibile a novembre 2020, consentirà proprietà di sola lettura (o meglio inizializzabili una volta) che è possibile inizializzare in un inizializzatore di oggetti. Questo non è attualmente possibile con le proprietà solo getter.

public string Name { get; init; }

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

2
Ottima risposta, grazie. Per l'operazione db sarebbe simile a questo: IQueryable<T> result = db.Set<T>().Find(//just id here//).ToList();Saprebbe già che stai cercando la chiave primaria. Solo per info.
Mr. Biondi

So che questa è una vecchia risposta, ma separerei il get e set in metodi diversi in modo che il valore non venga impostato accidentalmente durante un confronto.
Joel Trauger

@JoelTrauger: un confronto legge la proprietà e quindi chiama solo il getter.
Olivier Jacot-Descombes

Questo è vero, ma un'assegnazione accidentale chiamerà il setter e modificherà la proprietà. Vedere return object.property = valuevsreturn object.property == value
Joel Trauger

Anche una chiamata accidentale di un metodo set separato modificherà la proprietà. Non vedo come metodi di acquisizione separati possano migliorare la sicurezza.
Olivier Jacot-Descombes

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

o se ce n'è solo uno e vuoi imporre che qualcosa di simile SingleOrDefaultpossa essere quello che vuoi

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

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

Perché utilizzerai singleOrDefault se vuoi lanciare un'eccezione, usa Single ()
Nome in codice Jack


6

O se non preferisci usare LINQ puoi farlo alla vecchia maniera:

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

Puoi anche usare le estensioni LINQ :

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

4
o l'altro sovraccarico di First:MyClass result = list.First(m => m.GetId() == id);
Marcel Gosselin

3

Puoi risolvere il tuo problema in modo più conciso con un predicato scritto utilizzando la sintassi del metodo anonimo:

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

Sebbene questo codice possa rispondere alla domanda, è meglio spiegare come risolvere il problema e fornire il codice come esempio o riferimento. Le risposte di solo codice possono creare confusione e mancare di contesto.
Robert Columbia,

0

Puoi creare una variabile di ricerca per mantenere i tuoi criteri di ricerca. Ecco un esempio utilizzando database.

 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

Qui "var query" sta generando i criteri di ricerca forniti tramite la variabile di ricerca. Quindi "DuePaymentstring.Select" seleziona i dati che corrispondono ai criteri specificati. Sentiti libero di chiedere se hai problemi di comprensione.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.