Como posso encontrar um elemento específico em uma Lista <T>?

114

Meu aplicativo usa uma lista como esta:

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

Usando o Addmétodo, outra instância de MyClassé adicionada à lista.

MyClass fornece, entre outros, os seguintes métodos:

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

Como posso encontrar uma instância específica de MyClasspor meio do uso do GetIdmétodo? Eu sei que existe o Findmétodo, mas não sei se daria certo aqui ?!

Robert Strauch
fonte

Respostas:

263

Use uma expressão lambda

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

Nota: C # tem uma sintaxe interna para propriedades. Em vez de escrever métodos getter e setter (como você deve estar acostumado em Java), escreva

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

valueé uma palavra-chave contextual conhecida apenas no acessador set. Representa o valor atribuído à propriedade.

Como esse padrão é frequentemente usado, C # fornece propriedades implementadas automaticamente . Eles são uma versão resumida do código acima; no entanto, a variável de apoio está oculta e não acessível (ela é acessível de dentro da classe em VB, no entanto).

public string Id { get; set; }

Você pode simplesmente usar as propriedades como se estivesse acessando um 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 propriedades, você pesquisaria por itens na lista como este

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

Você também pode usar propriedades implementadas automaticamente se precisar de uma propriedade somente leitura:

public string Id { get; private set; }

Isso permite que você defina o Iddentro da classe, mas não de fora. Se você precisa configurá-lo em classes derivadas também, você também pode proteger o configurador

public string Id { get; protected set; }

E, finalmente, você pode declarar propriedades como virtuale substituí-las em classes derivadas, permitindo fornecer diferentes implementações para getters e setters; assim como para métodos virtuais comuns.


Desde C # 6.0 (Visual Studio 2015, Roslyn), você pode escrever propriedades automáticas somente getter com um inicializador embutido

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

Você também pode inicializar propriedades somente getter dentro do construtor. As propriedades automáticas somente getter são verdadeiras propriedades somente leitura, ao contrário das propriedades implementadas automaticamente com um configurador privado.

Isso também funciona com propriedades automáticas de leitura e gravação:

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

A partir do C # 6.0, você também pode escrever propriedades como membros com corpo de expressão

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

Consulte: .NET Compiler Platform ("Roslyn")
         Novos recursos de linguagem em C # 6

A partir do C # 7.0 , getter e setter podem ser escritos com corpos de expressão:

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

Observe que, neste caso, o setter deve ser uma expressão. Não pode ser uma declaração. O exemplo acima funciona porque em C # uma atribuição pode ser usada como uma expressão ou como uma instrução. O valor de uma expressão de atribuição é o valor atribuído onde a própria atribuição é um efeito colateral. Isso permite que você atribua um valor a mais de uma variável de uma vez: x = y = z = 0é equivalente ax = (y = (z = 0)) e tem o mesmo efeito que as declarações x = 0; y = 0; z = 0;.

A próxima versão da linguagem, C # 9.0, provavelmente disponível em novembro de 2020, permitirá propriedades somente leitura (ou melhor inicializar uma vez) que você pode inicializar em um inicializador de objeto. Atualmente, isso não é possível com propriedades somente getter.

public string Name { get; init; }

var c = new C { Name = "c-sharp" };
Olivier Jacot-Descombes
fonte
2
Ótima resposta, obrigado. Para a operação do banco de dados, seria algo assim: IQueryable<T> result = db.Set<T>().Find(//just id here//).ToList();Ele já saberia que você está procurando a chave primária. Apenas para informações.
Sr. Blond,
Eu sei que esta é uma resposta antiga, mas separaria get e set em métodos diferentes para que o valor não fosse definido acidentalmente durante uma comparação.
Joel Trauger
@JoelTrauger: uma comparação lê a propriedade e, portanto, chama apenas o getter.
Olivier Jacot-Descombes,
Isso é verdade, mas uma atribuição acidental irá chamar o configurador e modificar a propriedade. Ver return object.property = valuevsreturn object.property == value
Joel Trauger
Uma chamada acidental de um método de conjunto separado também modificará a propriedade. Não vejo como métodos get set separados podem melhorar a segurança.
Olivier Jacot-Descombes
19
var list = new List<MyClass>();
var item = list.Find( x => x.GetId() == "TARGET_ID" );

ou se houver apenas um e você quiser garantir que algo como SingleOrDefaultpode ser o que você deseja

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

if ( item == null )
    throw new Exception();
Zellio
fonte
Por que você usará singleOrDefault se quiser lançar uma exceção? Use Single ()
Code Name Jack
10

Experimentar:

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

Ou se você não preferir usar o LINQ, pode fazê-lo da maneira tradicional:

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
fonte
4

Você também pode usar extensões LINQ :

string id = "hello";
MyClass result = list.Where(m => m.GetId() == id).First();
Guffa
fonte
4
ou a outra sobrecarga da Primeira:MyClass result = list.First(m => m.GetId() == id);
Marcel Gosselin
3

Você pode resolver seu problema de forma mais concisa com um predicado escrito usando a sintaxe de método anônimo:

MyClass found = list.Find(item => item.GetID() == ID);
David Heffernan
fonte
0
public List<DealsCategory> DealCategory { get; set; }
int categoryid = Convert.ToInt16(dealsModel.DealCategory.Select(x => x.Id));
Muhammad Armaghan
fonte
Embora este código possa responder à pergunta, é melhor explicar como resolver o problema e fornecer o código como um exemplo ou referência. Respostas apenas em código podem ser confusas e não ter contexto.
Robert Columbia
0

Você pode criar uma variável de pesquisa para manter seus critérios de pesquisa. Aqui está um exemplo usando banco de dados.

 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

Aqui, "var query" está gerando os critérios de pesquisa fornecidos por meio da variável de pesquisa. Então, "DuePaymentstring.Select" seleciona os dados que correspondem aos seus critérios fornecidos. Sinta-se à vontade para perguntar se você tiver problemas para entender.

Khandkar Asif Hossain
fonte