2013-04-26 3 views
9

Exact rules for variance validity немного расплывчаты и не являются конкретными. Я собираюсь перечислить правила для того, что делает тип действительным-ковариантно, и прикладывать некоторые запросы и личные аннотации к каждому из этих правил.Правила отклонения в C#

Тип является ковариантно корректными если:

1) тип указателя или необщего типа.

Указатели и не общие типы не являются вариантами в C#, за исключением массивов и не общих делегатов. Общие классы, структуры и перечисления являются инвариантными. Я здесь?

2) Тип массива T [], где T действителен ковариантно.

Таким образом, это означает, что если тип элемента T массива T[] ковариантен (опорный или массив типа элемента), то массив ковариантен, и если тип элемента инвариантно (тип значения), то матрица тип инвариантен. Массивы не могут быть контравариантными в C#. Я здесь?

3) Тип типа типового типа, если он не был объявлен контравариантным.

Обычно мы говорим, что общий тип является вариантом по типу параметра, но для типа параметра, который должен быть вариантом на своем собственном. Это еще одна короткая форма для этого? например, общий тип T<out D> является ковариантным по D (следовательно, ковариантно действителен), поэтому можно сказать, что параметр типа D ковариантно действителен. Я прав?

4) Совокупный класс, структура, перечисление, интерфейс или делегат типа X может быть действительным ковариантно. Чтобы определить, является ли это, мы рассматриваем каждый аргумент типа по-разному, в зависимости от того, был ли указанный параметр типа объявлен как ковариантный (out), контравариантный (in) или инвариантный (ни один). (Конечно, типичные параметры типов классов и структур никогда не будут объявлены «вне» или «в», они всегда будут инвариантными.) Если параметр i-го типа был объявлен как ковариантный, то Ti должен быть действительным ковариантно. Если это было объявлено контравариантным, то Ti должно быть действительным контравариантно. Если он был объявлен как инвариант, то Ti должно быть действительным инвариантно.

Это последнее правило, сверху донизу, совершенно неоднозначно.

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

Перемещение вперед. Чтобы определить, является ли общий тип ковариантно действительным, мы рассмотрим его аргументы типа (не параметры типа). Поэтому, если соответствующий параметр типа является ковариантным/контравариантным/инвариантным, то аргумент типа действителен ковариантно/контравариантно/инвариантно, соответственно ...

Мне нужно, чтобы это правило объяснялось более подробно.


Редактировать: Thanks Eric. Очень ценим!

Я прекрасно понимаю, что действительное ковариантно/контравариантно/инвариантно означает. Тип действителен ковариантно, если он определенно не контравариантен, а значит, он может быть инвариантным. прекрасно!

Для 4-го правила вы следуете процедуре определения того, является ли построенный общий тип действительным ковариантно, как определено в правиле. Но как вы определяете, является ли аргумент типа, объявленный как ковариантный (out), ковариантно действительным?

Например, в закрытом сконструированной интерфейсе I {} из общего интерфейса I {...}, не должен сам факт, что аргумент типа object объявлен как ковариантном типа параметр (out U) в описании общего интерфейса означает, что объект аргумента типа является ковариантным? Думаю, так и должно быть. Потому что это само определение ковариантности.

Кроме того, второе правило:

2) Тип массива T [], где Т является ковариантно корректным.

Что элемент массива типа T быть ковариантно корректными означает? Вы имеете в виду тип элемента, являющийся значением типа (инвариантным в этом случае) или ссылочным типом (ковариантным в этом случае)?

Cuz проекция TT[] только вариант, если T является ссылочным типом.

+0

Повторить попытку? :) – Gjeltema

+0

Это действительно не похоже на то, что Эрик Липперт был либо расплывчатым, либо двусмысленным. Я буду ждать, пока он весит, прежде чем проверять глубину этой маленькой лужи. –

+0

Этот пост отличается от Gjeltema. В моем предыдущем сообщении о различии было разъяснено мое понимание дисперсии и связанные с ней термины короткой формы. –

ответ

12

Вы правы, что последнее правило является самым трудным для понимания, но я заверяю вас, что это не двусмысленно.

Пример или два помогут.Рассмотрим этот тип декларации:

interface I<in T, out U, V> { ... } 

Является ли этот тип ковариантно действует?

I<string, object, int> { } 

Давайте рассмотрим наше определение.

Для того, чтобы определить, является ли это, мы рассмотрим каждый аргумент типа по-разному, в зависимости от того, был ли соответствующий параметр типа объявлен ковариантным (уходит), контравариантные (в), или инвариантными (ни).

ОК, так что аргументы типа являются string, object и int. Соответствующие параметры: in T, out U и V, соответственно.

Если параметр i-го типа был объявлен как ковариантный (out), то Ti должен быть действительным ковариантно.

Второй параметр типа out U, поэтому object должен быть ковариантна корректными. Это.

Если это было объявлено контравариантным (in), то Ti должно быть действительным контравариантно.

Первый был объявлен in T, поэтому string должен быть контрвариантно. Это.

Если оно было объявлено как инвариантное, то Ti должно быть действительным инвариантно.

Третий V инвариантно, поэтому int должно быть инвариантно корректным; он должен быть как действительным, так и ковариантно. Это.

Мы передаем все три чека; тип I<string, object, int> действителен ковариантно.

OK, это был легко.

Теперь давайте рассмотрим более сложную задачу.

interface IEnumerable<out W> { ... } 
interface I<in T, out U, V> 
{ 
    IEnumerable<T> M(); 
} 

IEnumerable<T> внутри I является типом. Есть IEnumerable<T> как используется внутри I действительный ковариантно?

Давайте рассмотрим наше определение. У нас есть аргумент типа T, соответствующий типу параметра out W. Отметим, что T является типом параметр от I и тип аргумент от IEnumerable.

Если параметр го типа (W) объявлен ковариантным (out), то Ti (T) должен быть ковариантно корректным.

ОК, так что для IEnumerable<T> в I быть ковариантно корректными, T должны быть ковариантно корректными. Это? NO. T был объявлен как in T. Параметр типа, объявленный in, никогда не действует ковариантно. Поэтому тип IEnumerable<T>, используемый внутри I, недействителен ковариантно, так как условие «must» нарушено.

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

+0

Спасибо Эрик. Можете ли вы ответить на два новых вопроса SMALL, добавленных в конце моего первоначального сообщения? –

4

Нет, ваши аннотации перепутаны.

Эта статья действительно трудно понять, отчасти потому, что «действительный ковариантно» не имеет ничего общего с ковариацией. Эрик указывает на это, но это означает, что для каждого предложения вам нужно «осмыслить» естественный смысл, затем подумайте в терминах этих странных определений «действительный ковариантно», «действительный контравариантно» и «действительный инвариантно».

Я настоятельно рекомендую вам вместо этого прочитать о Принципе замещения Лискова, а затем подумать о взаимозаменяемости. Ковариантность, контравариантность и инвариантность имеют очень простые определения при взгляде с точки зрения LSP.

Тогда вы можете заметить, что правила C# во время компиляции точно не совпадают с LSP (к сожалению - и это в основном ошибка, сделанная на Java и скопированная на C#, чтобы помочь разработчикам Java). С другой стороны, во время выполнения должны соблюдаться правила LSP, поэтому, если вы начнете с них, вы напишете код, который компилирует и правильно выполняет, что, я думаю, более целесообразно, чем изучение правил языка C# (если вы не пишете компилятор C#).

4

Как вы определяете, является ли аргумент типа, объявленный как ковариантный (out) ковариантно действительным?

чтение Правило 3.

в закрыто построен интерфейс I{string, object int> из общего интерфейса I<in T, out U, V>, не должен сам факт того, что тип аргумент object объявляются в качестве параметра ковариантного типа out U в объявление универсального интерфейса означает, что аргумент типа object является ковариантным?

Во-первых, вы используете «ковариант», где вы имеете в виду «ковариантно действительный». Помните, что это разные вещи.

Во-вторых, давайте рассмотрим его снова. Is object covariantly действительно? Да, по правилу 1. Является ли I<string, object, int> ковариантно действительным? Да, согласно правилу 3, в котором говорится, что:

  • Аргумент типа, соответствующий T, должен быть контравариантно действительным.
  • Аргумент типа, соответствующий U, должен быть ковариантно действительным.
  • Аргумент типа, соответствующий V, должен быть обоим.

Поскольку все три условия выполнены, I<string, object, int> ковариантно действует.

В разделе «Тип массива T [], где T действителен ковариантно« что означает, что тип элемента массива T действителен ковариантно?

Непонятный вопрос. Мы имеем , определяющий, что означает «ковариантно действительный». Правило 2 является частью определения «ковариантно действительным».

Например, is object[] covariantly valid? Да, потому что object ковариантно действителен.Если бы мы имели:

interface IFoo<out T> { T[] M(); } 

ли T[] ковариантно действительным? Да, потому что T ковариантно действует.

Если бы мы имели

interface IBar<in T> { T[] M(); } 

ли T[] ковариантно действительным? Нет. Для того чтобы тип массива был ковариантно действительным, его тип элемента должен быть ковариантно действительным, но T - нет.

+0

Получил! Я действительно подсознательно смешивал разные термины друг с другом. Оценил Эрик. –

Смежные вопросы