C # Generics - out keyword - 'метод имеет неправильный тип возврата' [duplicate]

Если вы недавно изменили innodb_log_file_size , попытайтесь восстановить предыдущее значение, которое сработало.

128
задан Cuong Le 19 September 2012 в 08:26
поделиться

4 ответа

В принципе, дисперсия применяется, когда CLR может гарантировать, что ей не нужно делать какие-либо репрезентативные изменения к значениям. Все ссылки одинаковы, поэтому вы можете использовать IEnumerable<string> как IEnumerable<object> без каких-либо изменений в представлении; сам собственный код не обязательно должен знать, что вы делаете со значениями, если инфраструктура гарантировала, что он определенно будет действительным.

Для типов значений, которые не work - для обработки IEnumerable<int> как IEnumerable<object>, код, использующий последовательность, должен был бы знать, следует ли выполнять преобразование бокса или нет.

Возможно, вы захотите прочитать блог Эрика Липперта пост по представлению и идентичности для более подробной информации по этой теме в целом.

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

Вот почему ковариантные и контравариантные преобразования интерфейса и типов делегатов требуют, чтобы все переменные аргументы типа были ссылочными типами. Чтобы гарантировать, что вариантное преобразование ссылок всегда сохраняется с сохранением идентичности, все преобразования, связанные с аргументами типа, также должны сохраняться в идентичности. Самый простой способ гарантировать, что все нетривиальные преобразования в аргументах типа сохраняют сохранение идентичности, - это ограничение их ссылок на ссылки.

106
ответ дан Jon Skeet 17 August 2018 в 09:44
поделиться
  • 1
    Спасибо за ваш ответ, ссылка от Эрика Липперта очень ценна, я все еще немного запутался в вашем заявлении. variance применяется, когда CLR может гарантировать, что ему не нужно делать какие-либо репрезентативные изменения значений ? это первый критерий применения дисперсии? – Cuong Le 17 September 2012 в 09:16
  • 2
    @CuongLe: Ну, это детализация реализации в некоторых смыслах, но я считаю, что это основная причина ограничения. – Jon Skeet 17 September 2012 в 09:25
  • 3
    Я не думаю, что это имеет какое-либо отношение к представлению: int не является подтипом object, поэтому ковариантное преобразование не требуется. Напротив, IEnumerable<Integer> будет ковариантным типом возврата. – André Caron 17 September 2012 в 13:07
  • 4
    @ AndréCaron: сообщение в блоге Эрика здесь важно - это не просто представление, но и сохранение идентичности. Но сохранение сохранности означает, что сгенерированный код вообще не должен заботиться об этом. – Jon Skeet 17 September 2012 в 13:54
  • 5
    Точно, идентичность не может быть сохранена, потому что int не является подтипом object. Следствием этого является тот факт, что требуется репрезентативное изменение. – André Caron 17 September 2012 в 18:16

Я думаю, что все начинается с определения LSP (Принцип замещения Лискова), который climes:

, если q (x) - свойство, доказуемое об объектах x типа T, то q ( y) должно быть истинным для объектов y типа S, где S является подтипом T.

Но типы значений, например int, не могут быть заменены object в C#. Доказательство очень просто:

int myInt = new int();
object obj1 = myInt ;
object obj2 = myInt ;
return ReferenceEquals(obj1, obj2);

Это возвращает false, даже если мы назначим ту же «ссылку» на объект.

8
ответ дан Dean Kuga 17 August 2018 в 09:44
поделиться
  • 1
    Я думаю, что вы используете правильный принцип, но нет никаких доказательств: int не является подтипом object, поэтому принцип не применяется. Ваше & quot; доказательство & quot; опирается на промежуточное представление Integer, которое является подтипом object и для которого язык имеет неявное преобразование (object obj1=myInt; фактически расширен до object obj1=new Integer(myInt);). – André Caron 17 September 2012 в 13:05
  • 2
    Язык заботится о правильном кастинге между типами, но поведение ints не соответствует тому, которое мы ожидаем от подтипа объекта. – Tigran 17 September 2012 в 13:10
  • 3
    Все дело в том, что int не является подтипом object. Более того, LSP не применяется, потому что myInt, obj1 и obj2 относятся к трем различным объектам: одному int и двум (скрытым) Integer s. – André Caron 17 September 2012 в 18:13
  • 4
    @ Андре: C # не Java. Ключевое слово C # int является псевдонимом для BCL System.Int32, который на самом деле является подтипом object (псевдоним System.Object). Фактически, базовый класс int - System.ValueType, базовый класс которого System.Object. Попробуйте оценить следующее выражение и посмотрите: typeof(int).BaseType.BaseType. Причина, по которой ReferenceEquals возвращает false, заключается в том, что int помещается в два отдельных окна, а идентификатор каждого окна отличается для любого другого поля. Таким образом, две операции по боксу всегда дают два объекта, которые никогда не идентичны, независимо от значения в коробке. – Allon Guralnek 18 September 2012 в 22:43
  • 5
    @AllonGuralnek: каждый тип значения (например, System.Int32 или List<String>.Enumerator) фактически представляет два вида вещей: тип местоположения хранилища и тип объекта кучи (иногда называемый «тип в штучной упаковке»). Место хранения, чьи типы получены из System.ValueType, будет содержать первое; объекты кучи, типы которых аналогичным образом будут удерживать последний. На большинстве языков существует расширяющаяся личность из прежней последней и сужение от последнего к первому. Обратите внимание, что, в то время как типы значений в штучной упаковке имеют дескриптор того же типа, что и места хранения типа значения, ... – supercat 20 November 2012 в 18:37

Он доходит до детали реализации: типы значений реализуются по-разному для ссылочных типов.

Если вы принудительно применяете типы значений как ссылочные типы (то есть оставьте их, например, обратившись к ним через интерфейс), вы можете получить дисперсию.

Самый простой способ увидеть разницу - просто рассмотреть Array: массив типов значений объединяется в память смежно (напрямую), где в качестве массива В ссылочных типах всегда имеется ссылка (указатель) в памяти; объекты, на которые указываются, выделены отдельно.

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

(*) Это может рассматриваться как одна и та же проблема. .

2
ответ дан Mark Hurd 17 August 2018 в 09:44
поделиться

Возможно, вам легче понять, если вы думаете о базовом представлении (хотя это действительно детализация реализации). Вот набор строк:

IEnumerable<string> strings = new[] { "A", "B", "C" };

Вы можете думать о том, что strings имеет следующее представление:

[0] : string reference -> "A"
[1] : string reference -> "B"
[2] : string reference -> "C"

Это набор из трех элементов, каждый являясь ссылкой на строку. Вы можете передать это в коллекцию объектов:

IEnumerable<object> objects = (IEnumerable<object>) strings;

В основном это то же представление, за исключением того, что ссылки являются ссылками на объекты:

[0] : object reference -> "A"
[1] : object reference -> "B"
[2] : object reference -> "C"

Представление такое же. Ссылки рассматриваются только по-разному; вы больше не можете получить доступ к свойству string.Length, но вы все равно можете вызвать object.GetHashCode(). Сравните это с коллекцией ints:

IEnumerable<int> ints = new[] { 1, 2, 3 };
[0] : int = 1
[1] : int = 2
[2] : int = 3

Чтобы преобразовать это в IEnumerable<object>, данные должны быть преобразованы путем бокса ints:

[0] : object reference -> 1
[1] : object reference -> 2
[2] : object reference -> 3

Это преобразование требует больше, чем литье.

8
ответ дан Martin Liversage 17 August 2018 в 09:44
поделиться
  • 1
    Бокс - это не просто «детали реализации». Типы в штучной упаковке хранятся так же, как объекты класса, и ведут себя, насколько может сказать внешний мир, подобно объектам класса. Единственное различие заключается в том, что в определении типа boxed value this относится к структуре, поля которой накладываются на те объекты кучи, которые хранят ее, а не ссылаются на объект, который их удерживает. Нет чистого пути для экземпляра типа boxed value, чтобы получить ссылку на объект кучи-оболочки. – supercat 21 November 2012 в 00:31
Другие вопросы по тегам:

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