#.net #vb.net #clr #union
#.net #vb.net #clr #объединение
Вопрос:
Я попытался поэкспериментировать со способностью .NET поддерживать объединения, используя приведенный ниже код, но это приводит к сбою системы.Исключение ExecutionEngineException в .NET 2.0 и ошибка FatalExecutionEngineError в .NET 4.0 с сообщением:
Среда выполнения обнаружила неустранимую ошибку. Адрес ошибки был 0x738b3138, в потоке 0x1080. Код ошибки 0xc0000005. Эта ошибка может быть ошибкой в среде CLR или в небезопасных или не поддающихся проверке частях пользовательского кода. Распространенные источники этой ошибки включают ошибки маршалинга пользователя для COM-interop или PInvoke, которые могут повредить стек.
Я согласен, что этот код не должен работать, но я не ожидал такого исключения. Это ошибка .NET?
Class POLine
Public price As Decimal
Public VendorItem As String
End Class
Class SOLine
Public price As Decimal
Public Required As DateTime
End Class
<System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Explicit)> _
Structure LineRef
<System.Runtime.InteropServices.FieldOffset(0)> _
Public poLine As POLine
<System.Runtime.InteropServices.FieldOffset(0)> _
Public soLine As SOLine
End Structure
Sub Main()
Dim lr As New LineRef
lr.poLine = New POLine With {.price = 1.23D, .VendorItem = "X22"}
lr.soLine = New SOLine With {.price = 3.14D, .Required = DateTime.Now}
Console.WriteLine("{0} {1}", lr.soLine.price, lr.soLine.Required)
Console.WriteLine("{0} {1}", lr.poLine.price, lr.poLine.VendorItem)
End Sub
Ответ №1:
Ваш код не поддается проверке, поскольку ссылки на объекты не должны перекрываться, что, очевидно, делают ваши poLine
и soLine
поля. Как указано в разделе 10.7 раздела II спецификации ECMA CLI:
Значения смещения должны быть неотрицательными. Таким образом возможно перекрывать поля, хотя смещения, занимаемые ссылкой на объект, не должны перекрываться со смещениями, занимаемыми типом встроенного значения или частью другой ссылки на объект. Хотя одна ссылка на объект может полностью перекрывать другую, это непроверяемо.
Вы можете дополнительно проверить, что ваш код не поддается проверке, используя PEVerify (выполнить peverify.exe для вашего исполняемого файла из командной строки Visual Studio) — это подтвердит, что код содержит ошибку.
Поэтому неудивительно, что вы видите исключение во время выполнения, и исключение, которое вы видите, кажется мне совершенно разумным (текст указывает, что оно может быть вызвано непроверяемым кодом) — что заставляет вас ожидать другого поведения?
Комментарии:
1. Я не знал, что в .NET / CLI существует категория кода, называемая «непроверяемый». Это что-то среднее между управляемым и небезопасным — категория кода, называемая «непроверяемый»? Правильно ли я помню, что вы можете предотвратить включение небезопасного кода в проект, если он не включен в «небезопасный» блок? Есть ли что-нибудь (возможно, похожее) за пределами PEVerify, что имеет дело с непроверяемым кодом? Существуют ли другие примеры непроверяемого кода, который не совсем «небезопасен»?
2. @BlueMonkMN — Я думаю, что «небезопасный» и «непроверяемый» являются синонимами. Действительно, сбивает с толку то, что использование
StructLayout
атрибута разрешено без/unsafe
переключателя в C # и VB.NET (в отличие от этого, в F # это, по крайней мере, генерирует предупреждение).3. В спецификации CLI используется только термин «непроверяемый». «Небезопасный» — это C #, и определяется исключительно спецификацией C #. На практике эти два термина пересекаются, но они не идентичны — как показывает этот вопрос, код, не поддающийся проверке с помощью CLI, может не попадать в категорию C #-unsafe. C #-unsafe, с другой стороны, всегда не поддается проверке.
Ответ №2:
Изменение смещения второго поля на 80 (или выше) решит вашу проблему.
Хотя я понятия не имею, что такое макет структуры и смещения, для чего вы вообще их используете?
Редактировать: О, только что заметил, что ваш вопрос заключается в том, почему это выдает эту ошибку, а не в том, что ее вызывает, и сеть 4.0 говорит, что ошибка может быть вызвана повреждением стека, и вы объявляете переменную с тем же смещением, что и первая, которая … повреждает стек. С этой ошибкой нет проблем.
Комментарии:
1. Я думал, что управляемый код был умнее этого и не допускал повреждения (которое считалось бы небезопасным) без явного предсказания его как небезопасного. Вот почему я запустил этот тест, чтобы посмотреть, как он будет обрабатывать попытку доступа к этой структуре потенциально небезопасным способом.
Ответ №3:
Вы сопоставляете два ссылочных типа с одним и тем же смещением. Это само по себе не является незаконным, если вы не пытаетесь использовать оба одновременно, что именно вы пытаетесь сделать здесь.
Это не ошибка, вы просто неправильно используете объединение, и система справедливо жалуется. В собственной среде указатель был бы автоматически перезаписан.
Комментарии:
1. Это именно моя точка зрения. Как я уже сказал, код не должен работать, но тип исключения, которое он выдает, не имеет смысла.
2. @Blue — Я понимаю вашу точку зрения; Я неправильно понял вопрос. Мне любопытно, где генерируется исключение? При попытке использовать перезаписанный экземпляр или сразу после его перезаписи? Если позже, то код по-прежнему технически безопасен, поскольку становится невозможным перезаписать другой экземпляр без того, чтобы управляемая среда немедленно не заметила и не выдала фатальную ошибку.
3. Это происходит на второй строке записи.
4. Черт. Тогда это довольно небезопасно.