C#  -  IReadOnlyCollections (revisitado)


  Hoje vamos rcordar como usar as interfaces genéricas somente leitura no C#.

Hoje veremos as interfaces genéricas somente leitura como IReadOnlyCollection, IReadOnlyDictionary e IReadOnlyList da plataforma .NET que permitem implementar coleções somente leitura.

Podemos aproveitar as interfaces genéricas somente leitura, como IReadOnlyList, IReadOnlyDictionary e IReadOnlyCollection para evitar modificações em coleções usadas em aplicações .NET Core.

Uma coleção representa um conjunto de objetos usados para armazenamento e recuperação de dados. As coleções permitem alocar memória dinamicamente para armazenar elementos e recuperá-los usando uma chave ou índice conforme necessário.

Você pode ter coleções padrão ou genéricas onde as coleções padrão não forneçam segurança de tipo e as coleções genéricas são seguras de tipo. As coleções padrão fazem parte do namespace System.Collections e as coleções genéricas fazem parte do namespace System.Collections.Generic.

Um objeto imutável é definido como um objeto que não pode ser alterado após ter sido criado. Nem todas as coleções são imutáveis, mas você pode usar os tipos de coleção somente leitura no .NET Core, como IReadOnlyList, IReadOnlyDictionary e IReadOnlyCollection para implementar tipos imutáveis. Todos eles fazem parte do namespace System.Collections.Generic.

O principal benefício destes recursos é que as novas interfaces são covariantes, exceto IReadOnlyDictionary. Isso significa que você pode usar um tipo derivado como parâmetro genérico ao passar uma coleção para um método definido para um tipo base. Se você tem uma classe Carro, por exemplo, que deriva de Veiculo, você pode ter um método que aceita um IReadOnlyList<Veiculo> e passa um IReadOnlyList<Carro>.

Vejamos um exemplo de código que demonstra o uso de vários membros da classe ReadOnlyCollection<T>.

Neste exemplo vamos criar um List<T> de strings e adicionar quatro nomes de carros a ele. A seguir vamos envolver a lista em um ReadOnlyCollection<T>.

Depois de demonstrar os membros Count, Contains, Item[] e IList.IndexOf, o exemplo mostra que o ReadOnlyCollection<T> é apenas um wrapper para o List<T> original adicionando um novo item ao List<T> e exibindo o conteúdo do ReadOnlyCollection<T>.

Por fim, vamos criar uma matriz maior que a coleção e usar o método CopyTo para inserir os elementos da coleção no meio da matriz.

Para isso vamos criar um projeto Console no .NET 6.

using System.Collections.ObjectModel;

Console.WriteLine("\n###  Usando ReadOnlyCollection<T>  ###\n");

//criando a lista de strings
List<string> carros = new List<string>();
carros.Add("Chevrolet");
carros.Add("Ford");
carros.Add("Honda");
carros.Add("Renault");

//envolvendo a lista em um ReadonlyCollection
ReadOnlyCollection<string> readOnlyCarros = new ReadOnlyCollection<string>(carros);

//exibindo os itens da lista
Console.WriteLine();
foreach (string carro in readOnlyCarros)
{
    Console.WriteLine(carro);
}

//contando os itens
Console.WriteLine("\nCount: {0}", readOnlyCarros.Count);

//verificando se a lista contém um item
Console.WriteLine($"\nContains(\"Honda\"): {readOnlyCarros.Contains("Honda")}");

Console.WriteLine($"\nreadOnlyCarros[3]: {readOnlyCarros[3]}");
Console.WriteLine($"\nIndexOf(\"Renault\"): {readOnlyCarros.IndexOf("Renault")}");

Console.WriteLine("\nInserir na lista:");
Console.WriteLine("Insert(2, \"Toyota\")");
carros.Insert(2, "Toyota");

Console.WriteLine();
foreach (string carro in readOnlyCarros)
{
    Console.WriteLine(carro);
}

//criando uma matriz maior que a coleção e copiando os elementos da coleção na matriz
string[] carroArray = new string[readOnlyCarros.Count + 2];
readOnlyCarros.CopyTo(carroArray, 1);

Console.WriteLine($"\nO array copiado tem {carroArray.Length} elementos:");
foreach (string carro in carroArray)
{
    Console.WriteLine($"\"{carro}\"");
}

Console.ReadKey();

 

Resultado da execução do código acima:



Uma instância da classe genérica ReadOnlyCollection<T> é sempre somente leitura, e , uma coleção que é somente leitura é simplesmente uma coleção com um invólucro que impede a modificação da coleção; portanto, se forem feitas alterações na coleção subjacente, a coleção somente leitura refletirá essas alterações.

Usando IReadOnlyList

Vejamos agora um exemplo de IReadOnlyList.

Vamos supor que temos a classe Aluno com um método estático que retorna uma lista de alunos e que defina a lista usando IReadOnlyList<Aluno> :

namespace CShp_IReadOnlyCollections;

public class Aluno
{
    public int Id { get; set; }
    public string? Nome { get; set; }
    public string? Email { get; set; }

    public static IReadOnlyList<Aluno> GetAlunos()
    {
       return new List<Aluno>
       {
           new Aluno
           {
               Id = 1,
               Nome = "Maria",
               Email = "maria@email.com"
           },
           new Aluno
           {
               Id = 2,
               Nome = "Manoel",
               Email = "manoel@email.com"
           }
        };
    }
}

Na classe Program vamos incluir o código a seguir para acessar a lista e exibir os alunos:

using CShp_IReadOnlyCollections;

var alunos = Aluno.GetAlunos();

foreach(var aluno in alunos)
{
    Console.WriteLine($"{aluno.Nome} {aluno.Email}");
}

Console.Read();

Vamos verificar o tipo alunos que é um IReadOnlyList e verificamos não temos o método Add que permite incluir um elemento na lista justamente por ser uma lista somente leitura:

Com isso podemos evitar que os dados de uma coleção sejam alterados usando o tipo IReadOnlyList.

Pegue o projeto aqui:   CShp_IReadOnlyCollections.zip

"Sede unânimes entre vós; não ambicioneis coisas altas, mas acomodai-vos às humildes; não sejais sábios em vós mesmos;"
Romanos 12:16

Referências:


José Carlos Macoratti