Covariância C#

Resumo : neste tutorial, você aprenderá sobre a covariância C# que permite escrever código genérico mais flexível.

Introdução à covariância C#

A herança permite definir um relacionamento é-um entre classes . Por exemplo, um funcionário é uma pessoa. Portanto, você pode definir uma Employeeclasse que herda de uma Personclasse como esta:

class Person
{
    public string Name  { get; set;}
    public Person(string name)
    {
        Name = name;
    }
}

class Employee : Person
{
    public string JobTitle { get; set;}

    public Employee(string name, string jobTitle) : base(name)
    {
        JobTitle = jobTitle;
    }
}Linguagem de código:  C#  ( cs )

Em C#, covariância significa que a ordem é igual a uma herança. Por exemplo, uma lista de funcionários é uma lista de pessoas. Portanto, uma lista é covariante .

Por definição, a covariância permite que uma classe ou interface derivada retorne um tipo mais derivado que é retornado pela classe base ou método de interface. C# oferece suporte à covariância em interfaces genéricas e delegados .

Para definir uma covariância, você usa a outpalavra-chave. Por exemplo:

interface MyInterface<out T>
{
}Linguagem de código:  PHP  ( php )

A palavra-chave outsignifica que você só pode retornar Tdos métodos da interface. Em outras palavras, você não pode usar Tcomo parâmetro de nenhum método. Por exemplo:

interface MyInterface<out T>
{
    T GetNext();
}Linguagem de código:  PHP  ( php )

Mas o exemplo a seguir resultará em erro porque usamos Tcomo parâmetro do Add()método:

interface MyInterface<out T>
{
    T GetNext();

    // Invalid variance: The type parameter 'T' must be contravariantly
    // valid on 'MyInterface<T>.Add(T)'.'T' is covariant
    void Add(T item); // ERROR
}Linguagem de código:  PHP  ( php )

O .NET possui muitas interfaces genéricas integradas que são covariantes, por exemplo, a IEnumerable<T>interface:

public interface IEnumerable<out T> : System.Collections.IEnumerableLinguagem de código:  C#  ( cs )

Como IEnumerable<T>é uma covariância, você pode atribuir uma lista de Employeeobjetos a uma lista de Personobjetos como esta:

IEnumerable<Employee> employees = new List<Employee>()
{
    new Employee("John Doe","C# Developer"),
    new Employee("Jane Doe","UI/UX Designer")
};

IEnumerable<Person> people = employees;Linguagem de código:  C#  ( cs )

Este exemplo mostra que o Employeeobjeto é upcast para o Personobjeto. Se the IEnumerable<T>não for uma covariante, o código não será compilado.

Além disso, você pode passar uma lista de Employeeobjetos para um método que aceita uma lista de Personobjetos como no exemplo a seguir:

using static System.Console;

class Person
{
    public string Name
    {
        get; set;
    }
    public Person(string name)
    {
        Name = name;
    }
}

class Employee : Person
{
    public string JobTitle
    {
        get; set;
    }

    public Employee(string name, string jobTitle) : base(name)
    {
        JobTitle = jobTitle;
    }
}


class Program
{
    public static void Display(IEnumerable<Person> people)
    {

        foreach (var person in people)
        {
            WriteLine(person.Name);
        }
    }

    public static void Main(string[] args)
    {
        IEnumerable<Employee> employees = new List<Employee>()
        {
            new Employee("John Doe","C# Developer"),
            new Employee("Jane Doe","UI/UX Designer")
        };
        Display(employees);
    }
}Linguagem de código:  C#  ( cs )

Saída:

John Doe
Jane DoeLinguagem de código:  C#  ( cs )

Definindo uma interface covariante em C#

O exemplo a seguir define a IGroup<T>interface e Group<T>a classe que implementa a IGroup<T>interface.

using static System.Console;

class Person
{
    public string Name
    {
        get; set;
    }
    public Person(string name)
    {
        Name = name;
    }
}

class Employee : Person
{
    public string JobTitle
    {
        get; set;
    }

    public Employee(string name, string jobTitle) : base(name)
    {
        JobTitle = jobTitle;
    }
}

interface IGroup<out T>
{
    IEnumerable<T> GetAll();
}

class Group<T> : IGroup<T>
{
    private readonly List<T> list = new();
    public Group(List<T> list)
    {
        this.list = list;
    }
    public IEnumerable<T> GetAll() => list;
}

class Program
{
    public static void Display(IGroup<Person> people)
    {
        foreach (var person in people.GetAll())
        {
            WriteLine(person.Name);
        }
    }

    public static void Main(string[] args)
    {
        var employees = new List<Employee>()
        {
            new Employee("John Doe","C# Developer"),
            new Employee("Jane Doe","UI/UX Developer")
        };

        IGroup<Employee> employeeGroup = new Group<Employee>(employees);
        
        Display(employeeGroup);
    }
}Linguagem de código:  C#  ( cs )

Se você remover a palavra-chave out da interface IGroup<out T>, o programa não compilará e retornará o seguinte erro:

Cannot implicitly convert type 'IGroup<Employee>' to 'IGroup<Person>'. An explicit conversion exists (are you missing a cast?)Linguagem de código:  C#  ( cs )

Resumo

  • Covariância significa que quando você tem uma hierarquia de classes ou interfaces, um método em uma classe ou interface derivada pode retornar um tipo mais específico do que o mesmo método em sua classe base ou interface.
  • A covariância permite escrever código mais flexível e adaptável.
  • Use a palavra-chave out para definir uma covariância.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *