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 Employee
classe que herda de uma Person
classe 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 out
palavra-chave. Por exemplo:
interface MyInterface<out T>
{
}
Linguagem de código: PHP ( php )
A palavra-chave out
significa que você só pode retornar T
dos métodos da interface. Em outras palavras, você não pode usar T
como 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 T
como 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.IEnumerable
Linguagem de código: C# ( cs )
Como IEnumerable<T>
é uma covariância, você pode atribuir uma lista de Employee
objetos a uma lista de Person
objetos 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 Employee
objeto é upcast para o Person
objeto. 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 Employee
objetos para um método que aceita uma lista de Person
objetos 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 Doe
Linguagem 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.