C# - Interface IComparer


  Hoje veremos a interface IComparer da linguagem C#.

As interfaces IComparable e IComparer embora tenham nomes parecidos servem a diferentes propósitos.

Se você tiver uma coleção que já dá suporte a IComparer você vai poder ordenar a coleção sem precisar fornecer nenhuma referência explícita a IComparer. Nestes casos os elementos da coleção são convertidos para a implementação padrão de IComparer (Comparer.Default). No entanto, se você deseja fornecer capacidade de classificação ou comparação para seus objetos personalizados, deve implementar uma ou ambas as interfaces.

IComparer

A interface IComparer fornece o método Compare que compara dois objetos e retorna um valor indicando se um é menor, igual ou maior que o outro.

Obs: O método CompareTo da interface IComparable pode classificar usando apenas um campo por vez, portanto, não é possível classificar propriedades diferentes com ele.

Assim, a função de IComparer é fornecer mais mecanismos de comparação. Por exemplo, talvez você queira fornecer a ordem de sua classe em vários campos ou propriedades, ordem crescente e decrescente no mesmo campo ou ambos.

Essa interface é usada com os métodos List.Sort e List.BinarySearch, e  fornece uma maneira de personalizar a ordem de classificação de uma coleção. As classes SortedDictionary e SortedList implementam essa interface.

Vejamos um exemplo de uso desta interface.

Suponha que temos uma lista de funcionários com salários e que desejamos ordenar a lista pelo valor dos salários.

var funcionarios = new List<(string, int)>
{
   (
"José Antunes ", 1230),
   (
"Adam Nunes", 670),
   (
"Roberto Caiado", 2300),
   (
"Rose Amado", 990),
   (
"Jaime Santos", 1190),
   (
"Janete Bueno", 980),
   (
"Lucia Silveira", 980),
   (
"Tomas Sanches", 1400)
};

Aqui estamos usando uma tupla que é uma estrutura de dados que contém uma sequenência de elementos de diferentes tipos. A classe Tuple() fornece métodos estáticos para criar objetos de coleção de itens.

Se aplicarmos o método Sort() veremos que a ordenação vai ser feita pelo nome do funcionário pois estamos usando o comparador padrão.

funcionarios.Sort();
funcionarios.ForEach(funci => Console.WriteLine(funci));

Assim teremos que usar a interface IComparer e implementar o método Compare onde vamos definir a ordenação com base no salário do funcionário.

Vamos criar para isso a classe SalarioComparer :

class SalarioComparer : IComparer<(string, int)>
{
  
public int Compare((string, int) funci1, (string, int) funci2)
   {
    
return funci1.Item2.CompareTo(funci2.Item2);
   }
}

A seguir podemos usar o construtor Sort(new SalarioComparer) e passar a instância de SalarioComparer onde temos a comparação por salário implementada :

var funcionarios = new List<(string, int)>
{
    ("José Antunes ", 1230),
    ("Adam Nunes", 670),
    ("Roberto Caiado", 2300),
    ("Rose Amado", 990),
    ("Jaime Santos", 1190),
    ("Janete Bueno", 980),
    ("Lucia Silveira", 980),
    ("Tomas Sanches", 1400)
};
funcionarios.Sort(new SalarioComparer());
Console.WriteLine("Ordem salarial\n");
funcionarios.ForEach(funci => Console.WriteLine(funci));
Console.ReadKey();
class SalarioComparer : IComparer<(string, int)>
{
    public int Compare((string, int) funci1, (string, int) funci2)
    {
        return funci1.Item2.CompareTo(funci2.Item2);
    }
}

O resultado obtido é dado abaixo:

Os métodos Order e OrderDescending da LINQ possuem variantes sobrecarregadas que usam o IComparer como parâmetro como mostra o exemplo a seguir:

var palavras = new List<string> {
           
"Mundo", "Guerra", "Livro", "Casa",
           
"floresta", "oceano", "Viveiro", "faca"
       };

Console.WriteLine("\nLinq - Order\n");
var
linqOrder = palavras.Order(StringComparer.OrdinalIgnoreCase).ToList();
linqOrder.ForEach(palavra => Console.WriteLine(palavra));

Console.WriteLine("\nLinq - OrderDescending\n");
var
linqOrderDescending = palavras.OrderDescending(StringComparer.OrdinalIgnoreCase).ToList();
linqOrderDescending.ForEach(w => Console.WriteLine(w));

Console.ReadKey();

Observe que estamos passando StringComparer.OrdinalIgnoreCase para os métodos.

StringComparer é um comparador embutido para comparar strings que representa uma operação de comparação de cadeia de caracteres que usa casos específicos e regras de comparação ordinal ou baseadas em cultura.

E estamos conversados...

"E vi um novo céu, e uma nova terra. Porque já o primeiro céu e a primeira terra passaram, e o mar já não existe."
Apocalipse 21:1

Referências:


José Carlos Macoratti