Пример ковариантности и контравариантности в реальном мире

Все объекты в вашем примере возвращают объекты, поскольку все объекты являются объектами в .NET; int и bool - объекты. Если вы имеете в виду ссылочный тип, в отличие от типов значений, то вы можете сделать следующее:

foreach (PropertyInfo pi in typeof(Client).GetProperties()) {
    if (pi.PropertyType.IsClass) {
        // reference type
        // DoMyFunkyStuff
    }
}
140
задан a14m 30 January 2015 в 22:45
поделиться

3 ответа

Допустим, у вас есть класс Person и производный от него класс, Учитель. У вас есть несколько операций, которые принимают IEnumerable в качестве аргумента. В вашем классе School у вас есть метод, который возвращает IEnumerable . Ковариация позволяет вам напрямую использовать этот результат для методов, которые принимают IEnumerable , заменяя более производный тип на менее производный (более общий) тип. Контравариантность, как это ни парадоксально, позволяет вам использовать более общий тип, в котором указан более производный тип.

См. Также Ковариация и контравариантность в универсальных шаблонах на MSDN .

Классы :

public class Person 
{
     public string Name { get; set; }
} 

public class Teacher : Person { } 

public class MailingList
{
    public void Add(IEnumerable<out Person> people) { ... }
}

public class School
{
    public IEnumerable<Teacher> GetTeachers() { ... }
}

public class PersonNameComparer : IComparer<Person>
{
    public int Compare(Person a, Person b) 
    { 
        if (a == null) return b == null ? 0 : -1;
        return b == null ? 1 : Compare(a,b);
    }

    private int Compare(string a, string b)
    {
        if (a == null) return b == null ? 0 : -1;
        return b == null ? 1 : a.CompareTo(b);
    }
}

Использование :

var teachers = school.GetTeachers();
var mailingList = new MailingList();

// Add() is covariant, we can use a more derived type
mailingList.Add(teachers);

// the Set<T> constructor uses a contravariant interface, IComparer<T>,
// we can use a more generic type than required.
// See https://msdn.microsoft.com/en-us/library/8ehhxeaf.aspx for declaration syntax
var teacherSet = new SortedSet<Teachers>(teachers, new PersonNameComparer());
103
ответ дан 23 November 2019 в 22:27
поделиться
class A {}
class B : A {}

public void SomeFunction()
{
    var someListOfB = new List<B>();
    someListOfB.Add(new B());
    someListOfB.Add(new B());
    someListOfB.Add(new B());
    SomeFunctionThatTakesA(someListOfB);
}

public void SomeFunctionThatTakesA(IEnumerable<A> input)
{
    // Before C# 4, you couldn't pass in List<B>:
    // cannot convert from
    // 'System.Collections.Generic.List<ConsoleApplication1.B>' to
    // 'System.Collections.Generic.IEnumerable<ConsoleApplication1.A>'
}

Обычно всякий раз, когда у вас есть функция, которая принимает Enumerable одного типа, вы не можете передать Enumerable производного типа без явного его преобразования.

Просто чтобы предупредить вас о ловушке:

var ListOfB = new List<B>();
if(ListOfB is IEnumerable<A>)
{
    // In C# 4, this branch will
    // execute...
    Console.Write("It is A");
}
else if (ListOfB is IEnumerable<B>)
{
    // ...but in C# 3 and earlier,
    // this one will execute instead.
    Console.Write("It is B");
}

Это все равно ужасный код, но он существует, и изменение поведения в C # 4 может привести к появлению тонких и трудных для поиска ошибок, если вы используете такую ​​конструкцию.

32
ответ дан 23 November 2019 в 22:27
поделиться
// Contravariance
interface IGobbler<in T> {
    void gobble(T t);
}

// Since a QuadrupedGobbler can gobble any four-footed
// creature, it is OK to treat it as a donkey gobbler.
IGobbler<Donkey> dg = new QuadrupedGobbler();
dg.gobble(MyDonkey());

// Covariance
interface ISpewer<out T> {
    T spew();
}

// A MouseSpewer obviously spews rodents (all mice are
// rodents), so we can treat it as a rodent spewer.
ISpewer<Rodent> rs = new MouseSpewer();
Rodent r = rs.spew();

Для полноты…

// Invariance
interface IHat<T> {
    void hide(T t);
    T pull();
}

// A RabbitHat…
IHat<Rabbit> rHat = RabbitHat();

// …cannot be treated covariantly as a mammal hat…
IHat<Mammal> mHat = rHat;      // Compiler error
// …because…
mHat.hide(new Dolphin());      // Hide a dolphin in a rabbit hat??

// It also cannot be treated contravariantly as a cottontail hat…
IHat<CottonTail> cHat = rHat;  // Compiler error
// …because…
rHat.hide(new MarshRabbit());
cHat.pull();                   // Pull a marsh rabbit out of a cottontail hat??
128
ответ дан 23 November 2019 в 22:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: