¿Cómo puedo encontrar un elemento específico en una List <T>?

114

Mi aplicación usa una lista como esta:

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

Usando el Addmétodo, MyClassse agrega otra instancia de a la lista.

MyClass proporciona, entre otros, los siguientes métodos:

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

¿Cómo puedo encontrar una instancia específica MyClassde mediante el GetIdmétodo? Sé que existe el Findmétodo, ¡pero no sé si esto funcionaría aquí!

Robert Strauch
fuente

Respuestas:

263

Usa una expresión lambda

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

Nota: C # tiene una sintaxis incorporada para las propiedades. En lugar de escribir métodos getter y setter (como puede que esté acostumbrado en Java), escriba

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

valuees una palabra clave contextual conocida solo en el descriptor de acceso del conjunto. Representa el valor asignado a la propiedad.

Dado que este patrón se usa con frecuencia, C # proporciona propiedades implementadas automáticamente . Son una versión corta del código anterior; sin embargo, la variable de respaldo está oculta y no es accesible (sin embargo, es accesible desde dentro de la clase en VB).

public string Id { get; set; }

Simplemente puede usar las propiedades como si estuviera accediendo 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.

Usando propiedades, buscaría elementos en la lista como este

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

También puede usar propiedades implementadas automáticamente si necesita una propiedad de solo lectura:

public string Id { get; private set; }

Esto le permite configurar el Iddentro de la clase pero no desde fuera. Si necesita configurarlo también en clases derivadas, también puede proteger el establecedor

public string Id { get; protected set; }

Y finalmente, puede declarar propiedades como virtualy anularlas en clases derivadas, lo que le permite proporcionar diferentes implementaciones para getters y setters; al igual que para los métodos virtuales ordinarios.


Desde C # 6.0 (Visual Studio 2015, Roslyn), puede escribir propiedades automáticas solo para captadores con un inicializador en línea

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

En su lugar, también puede inicializar propiedades de solo captador dentro del constructor. Las propiedades automáticas de solo captador son verdaderas propiedades de solo lectura, a diferencia de las propiedades implementadas automáticamente con un configurador privado.

Esto también funciona con propiedades automáticas de lectura-escritura:

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

A partir de C # 6.0, también puede escribir propiedades como miembros con cuerpo de expresión

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

Consulte: Plataforma del compilador .NET ("Roslyn")
         Nuevas características del lenguaje en C # 6

A partir de C # 7.0 , tanto getter como setter se pueden escribir con cuerpos de expresión:

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

Tenga en cuenta que en este caso el establecedor debe ser una expresión. No puede ser una declaración. El ejemplo anterior funciona, porque en C # una asignación se puede usar como expresión o declaración. El valor de una expresión de asignación es el valor asignado donde la asignación en sí es un efecto secundario. Esto le permite asignar un valor a más de una variable a la vez: x = y = z = 0es equivalente x = (y = (z = 0))y tiene el mismo efecto que las declaraciones x = 0; y = 0; z = 0;.

La próxima versión del lenguaje, C # 9.0, probablemente disponible en noviembre de 2020, permitirá propiedades de solo lectura (o mejor inicializar una vez) que puede inicializar en un inicializador de objeto. Actualmente, esto no es posible con propiedades exclusivas de captadores.

public string Name { get; init; }

var c = new C { Name = "c-sharp" };
Olivier Jacot-Descombes
fuente
2
Gran respuesta, gracias. Para el funcionamiento de la base de datos, se vería así: IQueryable<T> result = db.Set<T>().Find(//just id here//).ToList();ya sabría que está buscando una clave principal. Sólo por información.
Mr. Blond
Sé que esta es una respuesta antigua, pero separaría get y set en diferentes métodos para que el valor no se establezca accidentalmente durante una comparación.
Joel Trauger
@JoelTrauger: una comparación lee la propiedad y, por lo tanto, solo llama al captador.
Olivier Jacot-Descombes
Esto es cierto, pero una asignación accidental llamará al establecedor y modificará la propiedad. Ver return object.property = valuevsreturn object.property == value
Joel Trauger
Una llamada accidental de un método de conjunto separado también modificará la propiedad. No veo cómo los métodos de configuración separados pueden mejorar la seguridad.
Olivier Jacot-Descombes
19
var list = new List<MyClass>();
var item = list.Find( x => x.GetId() == "TARGET_ID" );

o si solo hay uno y quieres hacer cumplir que algo como SingleOrDefaultpuede ser lo que quieres

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

if ( item == null )
    throw new Exception();
zellio
fuente
¿Por qué usará singleOrDefault si desea lanzar una excepción? Use Single ()
Nombre de código Jack
10

Tratar:

 list.Find(item => item.id==myid);
Amritpal Singh
fuente
6

O si no prefiere usar LINQ , puede hacerlo a la vieja usanza:

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
    }
}
Bjørn
fuente
4

También puede usar extensiones LINQ :

string id = "hello";
MyClass result = list.Where(m => m.GetId() == id).First();
Guffa
fuente
4
o la otra sobrecarga de Primera:MyClass result = list.First(m => m.GetId() == id);
Marcel Gosselin
3

Puede resolver su problema de la manera más concisa con un predicado escrito usando la sintaxis de método anónimo:

MyClass found = list.Find(item => item.GetID() == ID);
David Heffernan
fuente
0
public List<DealsCategory> DealCategory { get; set; }
int categoryid = Convert.ToInt16(dealsModel.DealCategory.Select(x => x.Id));
Muhammad Armaghan
fuente
Si bien este código puede responder a la pregunta, es mejor explicar cómo resolver el problema y proporcionar el código como ejemplo o referencia. Las respuestas de solo código pueden ser confusas y carecer de contexto.
Robert Columbia
0

Puede crear una variable de búsqueda para mantener sus criterios de búsqueda. Aquí hay un ejemplo usando una base de datos.

 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

Aquí "var query" genera los criterios de búsqueda que está dando a través de la variable de búsqueda. Luego, "DuePaymentstring.Select" selecciona los datos que coinciden con sus criterios dados. No dude en preguntar si tiene problemas de comprensión.

Khandkar Asif Hossain
fuente