Объявление указателя на определяемую классом структуру, где класс является универсальным

При использовании ключевого слова unsafe или fixed в C# вы можете определить указатели на неуправляемые типы, такие как byte* int* и т. д. Вы также можете определить указатель на любую структуру, содержащую только неуправляемые типы, например:

namespace a
{
   struct MyStruct 
   {
     int value1;
     int value2;
   }

   class b<T>
   {
      unsafe void SomeMethod()
      {
        MyStruct* ptr;
      }
   }
}

Однако, если struct определено внутри общего определения класса, я получаю сообщение об ошибке CS0208: Cannot take the address of, get the size of, or declare a pointer to a managed type. В чем причина этого ограничения?

ОБНОВЛЕНИЕ: эта ошибка возникает только в том случае, если содержащий класс является универсальным. Я по-прежнему не вижу причин для ошибки — компилятор видит, что структура всегда будет содержать неуправляемые типы, поскольку она не ссылается на универсальный тип T.

namespace a
{   
    class b<T>
    {
        struct MyStruct 
        {
            int value1;
            int value2;
        }

        unsafe void SomeMethod()
        {
            MyStruct* ptr; // gives a compiler error
        }
    }
}

ПРИМЕЧАНИЕ. Похоже, что эта функция будет добавлена ​​в C# в будущей версии: см. эта проблема на GitHub.


person Reinstate Monica    schedule 03.09.2016    source источник
comment
@Машинное обучение. Нет. Все поля здесь имеют тип значения - этот ответ относится к string, который не является.   -  person Reinstate Monica    schedule 04.09.2016
comment
Предоставьте хороший минимально воспроизводимый пример, который надежно воспроизводит описанную вами ошибку. Существует ряд сценариев, которые могут вызвать эту ошибку, но ни один из них, насколько я знаю, не начнется с кода, который вы показали здесь.   -  person Peter Duniho    schedule 04.09.2016
comment
@afuna структура int in struct может быть неназначенной, поэтому я предполагаю, что ее значение будет в куче при назначении и, следовательно, подлежит GC, как строка в классе ..   -  person    schedule 04.09.2016
comment
@Machine: int в структуре может быть неназначенным, поэтому я предполагаю, что его значение будет в куче - это утверждение не имеет смысла. Типы значений могут быть или не быть в куче, в зависимости от того, где они расположены (например, поле в классе или локальная переменная), но присвоено ли значение или нет, не имеет значения в месте его хранения или в памяти. из кучи было выделено.   -  person Peter Duniho    schedule 04.09.2016
comment
Поле @Peter Duniho в классе по сравнению с локальной переменной: локальная переменная типа значения (int) находится в стеке, и это не проблема для GC. То же самое, если у вас есть внутренний класс с int, все еще в порядке. Они в стеке. Внутренний класс со строкой ko, так как он управляется GC. Как насчет внутреннего класса с обнуляемым целым? Если это не разрешено, я предполагаю, что это похоже на внутреннюю структуру с целым числом, которое может быть не назначено   -  person    schedule 04.09.2016
comment
@Machine: во-первых, никто никогда не говорил ничего о int, допускающем значение NULL. Во-вторых, Nullable<T> — это тип значения, такой же, как int или любой определяемый пользователем struct, и работает точно так же с точки зрения хранения. И вы объединяете семантику типа (тип значения и ссылочный тип) с хранилищем (стек и куча). Эти две концепции на самом деле почти полностью не связаны между собой, несмотря на их взаимодействие друг с другом.   -  person Peter Duniho    schedule 04.09.2016
comment
Бокс используется для хранения типов значений в куче со сбором мусора, что я имею в виду, что это происходит и, возможно, вызывает проблему. Но я сдаюсь, я не могу настаивать. Это было мое предположение, может я ошибаюсь   -  person    schedule 04.09.2016
comment
@Machine: Nullable<T> вообще не включает бокс, и в коде, опубликованном в вопросе, нет бокса. Да, упакованный тип значения хранится в куче, но тогда это уже не тип значения; это ссылочный тип, который содержит тип значения.   -  person Peter Duniho    schedule 04.09.2016
comment
@PeterDuniho - спасибо за предложение по MCCV - это помогло мне сузить круг до дженериков. Но я все еще не вижу причины ошибки.   -  person Reinstate Monica    schedule 04.09.2016
comment
Что ж, вы правы, на самом деле нет необходимости отвергать этот код. Но у Эрика Липперта всегда есть идеальное оправдание, когда речь идет о крайних случаях в компиляторе C# и языке, для этого требуется дополнительный код для двойной проверки того, что члены структуры не используют аргумент типа. Дополнительный код, который необходимо написать, поддерживать и документировать. Учитывая, что обходной путь настолько прост, а потерянная функция настолько скромна, а вероятность того, что программист будет испытывать неудобства, так низка, будет довольно сложно заставить их добавить эту функцию. Вы, конечно, можете попробовать.   -  person Hans Passant    schedule 04.09.2016
comment
До вопрос был отредактирован с помощью дженериков, используя int? значение1; выдал бы точно такую ​​же ошибку. Я сделал разумное, обоснованное предположение, но не мог предвидеть будущее   -  person    schedule 04.09.2016


Ответы (1)


Я отредактировал ваш пример кода, чтобы он действительно мог воспроизвести ошибку.

Проблема здесь в том, что, хотя struct кажется допустимым неуправляемым типом, при вложении его в универсальный тип он становится «сконструированным типом», который считается управляемым типом. Это связано с тем, что полный тип вашего struct фактически включает параметр типа, а универсальные типы всегда являются управляемыми типами. т.е. тип не просто MyStruct, а скорее a.b<T>.MyStruct, где T - некоторый тип.

Из спецификации языка C# 5, "10.3.8.6 Вложенные типы в универсальных классах":

Каждое объявление типа, содержащееся в объявлении универсального класса, неявно является объявлением универсального типа.

"4.4 Составные типы" гласит:

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

И из "18.2 Типы указателей":

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

Другими словами, спецификация языка ясно дает понять, что MyStruct является "сконструированным типом", и что вам не разрешено иметь указатели на сконструированные типы.

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

В вашем примере параметр типа T не используется в MyStruct. Но это могло бы быть, и это было бы явно плохо в контексте указателя unsafe.

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

Этот последний пункт является ИМХО достаточным оправданием для разработчиков языка, чтобы просто исключить его. В конце концов, многие, если не большинство типов, вложенных в универсальный тип, в любом случае будут использовать параметр универсального типа, поэтому полезность такого дополнительного анализа и снисходительности, вероятно, ограничена.

person Peter Duniho    schedule 04.09.2016
comment
Спасибо за редактирование. Я скопировал и вставил и хотел вложить структуру, но, должно быть, все испортил. Что касается вашего ответа, я согласен, что это технически верно в соответствии со спецификацией. Что касается рациональности, мне трудно ее принять - компилятор всегда проверяет структуру в любом случае, чтобы убедиться, что ее поля неуправляемые - было бы проще просто классифицировать поле универсального типа как управляемое. , а когда нет, чтобы не кидать ошибку. - person Reinstate Monica; 04.09.2016
comment
Что касается рациональности, мне трудно ее принять -- вы профессиональный языковой дизайнер? Обоснование, которое вы ставите под сомнение, принадлежит людям, разработавшим C#, чрезвычайно компетентным экспертам в этой области. Я думаю, вполне вероятно, что у них есть очень веская причина сделать любой тип внутри универсального типа для себя сконструированным. Тем не менее, если вы хотите оспорить обоснование, вам нужно спорить с людьми, которые разработали язык, а не со мной. Лично я даю им презумпцию невиновности и предполагаю, что если спецификация что-то говорит, для этого была веская причина. - person Peter Duniho; 04.09.2016
comment
Конечно, у них может быть веская причина. Я имел в виду рациональное предложение, которое вы предложили - это усложнение в спецификации языка - это то, что мне трудно принять по причине, которую я написал в комментарии. Я ценю, что вы предложили рациональное решение, хотя вы и не дизайнер, и я не вижу проблем в обсуждении этого рационального решения с вас. - person Reinstate Monica; 04.09.2016
comment
@afuna: если вы думаете, что настроить спецификацию языка для решения этого конкретного крайнего случая так просто, я призываю вас сделать это. Возможно, после выполнения упражнения вам будет легче признать, что это бесполезно. - person Peter Duniho; 05.09.2016
comment
@afuna: у меня нет реальных оснований для обсуждения этого самого, потому что все, что я сделал, это дать разработчикам языка презумпцию невиновности и предположить, что они знали, что делали, когда делали это. У меня нет ни времени, ни необходимости пересматривать решения такого типа, поэтому я этого не делаю. Но точно так же у меня нет ни времени, ни необходимости защищать решение, кроме как дать разработчикам языка презумпцию невиновности. Мне жаль, что вы не нашли мой ответ полезным. - person Peter Duniho; 05.09.2016