Compartir a través de


Usar varianza en interfaces para colecciones genéricas (C#)

Una interfaz covariante permite que sus métodos devuelvan más tipos derivados que los especificados en la interfaz. Una interfaz contravariante permite que sus métodos acepten parámetros de tipos menos derivados que los especificados en la interfaz.

En .NET Framework 4, varias interfaces existentes se convirtieron en covariante y contravariante. Estos incluyen IEnumerable<T> y IComparable<T>. Esto le permite reutilizar métodos que funcionan con colecciones genéricas de tipos base para colecciones de tipos derivados.

Para obtener una lista de interfaces variantes en .NET, vea Varianza en interfaces genéricas (C#).

Conversión de colecciones genéricas

En el ejemplo siguiente se muestran las ventajas de la compatibilidad con la covarianza en la IEnumerable<T> interfaz. El PrintFullName método acepta una colección del IEnumerable<Person> tipo como parámetro. Sin embargo, puede volver a usarlo para una colección tipo IEnumerable<Employee> porque Employee hereda Person.

// Simple hierarchy of classes.  
public class Person  
{  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
}  
  
public class Employee : Person { }  
  
class Program  
{  
    // The method has a parameter of the IEnumerable<Person> type.  
    public static void PrintFullName(IEnumerable<Person> persons)  
    {  
        foreach (Person person in persons)  
        {  
            Console.WriteLine("Name: {0} {1}",  
            person.FirstName, person.LastName);  
        }  
    }  
  
    public static void Test()  
    {  
        IEnumerable<Employee> employees = new List<Employee>();  
  
        // You can pass IEnumerable<Employee>,
        // although the method expects IEnumerable<Person>.  
  
        PrintFullName(employees);  
  
    }  
}  

Comparación de colecciones genéricas

En el ejemplo siguiente se muestran las ventajas de la compatibilidad con la contravarianza en la interfaz IEqualityComparer<T>. La clase PersonComparer implementa la interfaz IEqualityComparer<Person>. Sin embargo, puede reutilizar esta clase para comparar una secuencia de objetos del Employee tipo porque Employee hereda Person.

// Simple hierarchy of classes.  
public class Person  
{  
    public string FirstName { get; set; }  
    public string LastName { get; set; }  
}  
  
public class Employee : Person { }  
  
// The custom comparer for the Person type  
// with standard implementations of Equals()  
// and GetHashCode() methods.  
class PersonComparer : IEqualityComparer<Person>  
{  
    public bool Equals(Person x, Person y)  
    {
        if (Object.ReferenceEquals(x, y)) return true;  
        if (Object.ReferenceEquals(x, null) ||  
            Object.ReferenceEquals(y, null))  
            return false;
        return x.FirstName == y.FirstName && x.LastName == y.LastName;  
    }  
    public int GetHashCode(Person person)  
    {  
        if (Object.ReferenceEquals(person, null)) return 0;  
        int hashFirstName = person.FirstName == null  
            ? 0 : person.FirstName.GetHashCode();  
        int hashLastName = person.LastName.GetHashCode();  
        return hashFirstName ^ hashLastName;  
    }  
}  
  
class Program  
{  
  
    public static void Test()  
    {  
        List<Employee> employees = new List<Employee> {  
               new Employee() {FirstName = "Michael", LastName = "Alexander"},  
               new Employee() {FirstName = "Jeff", LastName = "Price"}  
            };  
  
        // You can pass PersonComparer,
        // which implements IEqualityComparer<Person>,  
        // although the method expects IEqualityComparer<Employee>.  
  
        IEnumerable<Employee> noduplicates =  
            employees.Distinct<Employee>(new PersonComparer());  
  
        foreach (var employee in noduplicates)  
            Console.WriteLine(employee.FirstName + " " + employee.LastName);  
    }  
}  

Consulte también