Почему понятие “Ковариантности” и “Контравариантности” применимо при реализации методов интерфейса?

Вариант использования - некоторые что как это:

public class SomeClass : ICloneable
{
    // Some Code

    // Implementing interface method
    public object Clone()
    {
        // Some Clonning Code
    }
}

Теперь мой вопрос состоит в том, Почему не возможно использовать "SomeClass (Поскольку это получено из объекта)", как тип возврата Клона () метод, если мы рассматриваем Funda Ковариантности и Контравариантности?

Кто-то может объяснить меня причина позади этой реализации Microsoft????

7
задан dbc 12 December 2018 в 04:40
поделиться

6 ответов

Неразрушенная реализация вариации интерфейса-реализации должна быть ковариантной в типе возвращаемого значения и контравариантной в типах аргументов .

Например:

public interface IFoo
{
    object Flurp(Array array);
}

public class GoodFoo : IFoo
{
    public int Flurp(Array array) { ... }
}

public class NiceFoo : IFoo
{
    public object Flurp(IEnumerable enumerable) { ... }
}

И то, и другое разрешено "новыми" правилами, верно? Но как насчет этого:

public class QuestionableFoo : IFoo
{
    public double Flurp(Array array) { ... }
    public object Flurp(IEnumerable enumerable) { ... }
}

Трудно сказать, какая неявная реализация здесь лучше. Первый - точное совпадение с типом аргумента, но не с типом возвращаемого значения. Второй - точное совпадение с типом возвращаемого значения, но не с типом аргумента. Я склоняюсь к первому, потому что тот, кто использует интерфейс IFoo , может дать ему только массив , но это все еще не совсем понятно.

И это далеко не самое худшее. Что, если мы сделаем это вместо этого:

public class EvilFoo : IFoo
{
    public object Flurp(ICollection collection) { ... }
    public object Flurp(ICloneable cloneable) { ... }
}

Кто выиграет приз? Это вполне допустимая перегрузка, но ICollection и ICloneable не имеют ничего общего друг с другом, а Array реализует их оба. Я не вижу здесь очевидного решения.

Будет только хуже, если мы начнем добавлять перегрузки в сам интерфейс:

public interface ISuck
{
    Stream Munge(ArrayList list);
    Stream Munge(Hashtable ht);
    string Munge(NameValueCollection nvc);
    object Munge(IEnumerable enumerable);
}

public class HateHateHate : ISuck
{
    public FileStream Munge(ICollection collection);
    public NetworkStream Munge(IEnumerable enumerable);
    public MemoryStream Munge(Hashtable ht);
    public Stream Munge(ICloneable cloneable);
    public object Munge(object o);
    public Stream Munge(IDictionary dic);
}

Удачи в попытках разгадать эту тайну, не сойдя с ума.

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

Я не знаю, является ли это официальной причиной, по которой он не поддерживается в C # сегодня, но он должен служить хорошим примером того типа кода «только для записи», к которому он может привести, и частью Философия дизайна команды C # состоит в том, чтобы не дать разработчикам написать ужасный код.

6
ответ дан 6 December 2019 в 10:49
поделиться

С точки зрения объяснения причин, лежащих в основе решений C #, Эрик Липперт из Microsoft написал много объяснений Contra / CoVariance на C # ... вот список тегов из его блог: http://blogs.msdn.com/ericlippert/archive/tags/Covariance+and+Contravariance/default.aspx

{{1} }

[Edit] Возможно, это правильный пост, связанный с вашим вопросом .. http://blogs.msdn.com/ericlippert/archive/2007/10/26/covariance- and-contravariance-in-c-part-five-interface-variance.aspx

1
ответ дан 6 December 2019 в 10:49
поделиться

Согласно спецификации C #, вы должны использовать метод с идентичной сигнатурой при переопределении или реализации метода интерфейса. Имейте в виду, что Microsoft не владеет C #. Их компилятор C # - это просто их реализация. Так почему же спецификация поступает именно так? Я могу только догадываться, но подозреваю, что это было сделано для простоты реализации.

0
ответ дан 6 December 2019 в 10:49
поделиться

Вы должны реализовать методы интерфейса точно так, как они описаны в интерфейсе. Метод Clone в ICloneable возвращает объект, поэтому ваш SomeClass также должен возвращать объект. Однако вы можете без проблем вернуть экземпляр SomeClass в методе SomeClass's Clone, но определение метода должно соответствовать интерфейсу:

public class SomeClass: IClonable
 {
     // Some Code

     //Implementing interface method
     Public object Clone()
      {
        SomeClass ret = new SomeClass();
        // copy date from this instance to ret
        return ret;
      }
 }
2
ответ дан 6 December 2019 в 10:49
поделиться

Похоже, что они могли бы использовать дженерики, но, похоже, есть веская причина, почему они этого не сделали.

Об этом говорится здесь:

http://bytes.com/topic/c-sharp/answers/469671-generic-icloneable

По сути, общий интерфейс, который позволяет: открытый класс MyClass: IClonable

также разрешит: открытый класс MyClass: IClonable

, что на самом деле не дает никаких преимуществ и может сбивать с толку .

0
ответ дан 6 December 2019 в 10:49
поделиться

Позвольте мне перефразировать вопрос:

Такие языки, как C ++, позволяют переопределяющему методу иметь более конкретный тип возвращаемого значения, чем переопределенный метод. Например, если у нас есть типы

abstract class Enclosure {}
class Aquarium : Enclosure {}
abstract class Animal 
{
    public virtual Enclosure GetEnclosure();
}

, то это недопустимо в C #, но эквивалентный код будет разрешен в C ++:

class Fish : Animal
{
    public override Aquarium GetEnclosure() { ... 

Как называется эта функция C ++?

Эта функция называется «ковариация возвращаемого типа» . (Как указывает другой ответ, можно было бы также поддерживать «формальную контравариантность типов параметров», хотя C ++ не поддерживает.)

Почему это не поддерживается в C #?

Как я уже много раз отмечал, нам не нужно указывать причину, по которой функция не поддерживается; состояние по умолчанию для всех функций «не поддерживается». Только когда огромное количество времени и усилий вложено в реализацию, функция становится поддерживаемой. Скорее, функции, которые реализованы , должны иметь причины для них, и чертовски веские причины для этого, учитывая, сколько стоит их создание.

Тем не менее, есть два больших "аргумента" против этой функции, которые являются основными причинами, препятствующими ее реализации.

  1. CLR не поддерживает его. Чтобы эта работа работала, нам, по сути, пришлось бы реализовать метод точного соответствия, а затем создать вспомогательный метод, который его вызывает. Это выполнимо, но получается запутаться.

  2. Андерс считает, что это не очень хорошая языковая функция. Андерс - главный архитектор, и если он считает, что это плохая особенность, скорее всего, это не удастся. (Обратите внимание, мы думали, что именованные и необязательные параметры тоже не стоят затрат, но в конечном итоге это удалось.Иногда становится ясно, что вам действительно нужно сжать зубы и реализовать функцию, эстетика которой вам не очень нравится, чтобы удовлетворить реальный спрос.)

Короче говоря, определенно бывают моменты, когда это сработает. быть полезным, и это часто запрашиваемая функция. Однако вряд ли мы это сделаем. Преимущества функции не окупаются; это значительно усложняет семантический анализ методов, и у нас нет действительно простого способа его реализовать.

9
ответ дан 6 December 2019 в 10:49
поделиться
Другие вопросы по тегам:

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