Должно ли изменение содержимого строки как это вызвать исключение? - PullRequest
5 голосов
/ 23 декабря 2010

Рассмотрим следующий код:

using System;
using System.Runtime.InteropServices;

namespace Demo
{
    class Program
    {
        static void Main(string[] args)
        {
            const string test = "ABCDEF"; // Strings are immutable, right?
            char[] chars = new StringToChar{str=test}.chr;
            chars[0] = 'X';

            // On an x32 release or debug build or on an x64 debug build, 
            // the following prints "XBCDEF".
            // On an x64 release build, it prints "ABXDEF".
            // In both cases, we have changed the contents of 'test' without using
            // any 'unsafe' code...

            Console.WriteLine(test);
        }
    }

    [StructLayout(LayoutKind.Explicit)]
    public struct StringToChar
    {
        [FieldOffset(0)]
        public string str;
        [FieldOffset(0)]
        public char[] chr;
    }
}

Запустив этот код, мы можем изменять содержимое строки без исключений.Нам не нужно было объявлять небезопасный код для этого.Этот код явно очень изворотливый!

Мой вопрос прост: Вы думаете, что код, приведенный выше, должен вызвать исключение?

[EDIT1: Обратите внимание, что другие люди пытались этоя и некоторые люди получают разные результаты - что не слишком удивительно, учитывая мерзость того, что я делаю ...;)]

[EDIT2: обратите внимание, что я использую Visual Studio 2010 в Windows7 Ultimate 64 bit]

[EDIT3: Сделал тестовую строку const, просто чтобы сделать ее еще более хитрой!]

Ответы [ 2 ]

5 голосов
/ 23 декабря 2010

Исходный код SSCLI20 для clr / src / vm / class.cpp, MethodTableBuilder :: HandleExplicitLayout может предоставить некоторую информацию. Это необычно сильно комментируется, этот комментарий описывает правила (отредактированы для удобства чтения):

// go through each field and look for invalid layout
// (note that we are more permissive than what Ecma allows. We only disallow 
// the minimum set necessary to close security holes.)
//
// This is what we implement:
//
// 1. Verify that every OREF is on a valid alignment
// 2. Verify that OREFs only overlap with other OREFs.
// 3. If an OREF does overlap with another OREF, the class is marked unverifiable.
// 4. If an overlap of any kind occurs, the class will be marked NotTightlyPacked (affects ValueType.Equals()).

Правило 1 гарантирует, что эталонное назначение остается атомарным. Правило 2 говорит, почему вы можете делать то, что делали, любая ссылка на тип объекта может перекрываться. Наложение перекрытия со значением типа значения не допускается, что приводит к повреждению сборщика мусора. Правило 3 гласит следствие: оно только делает тип не подлежащим проверке.

В противном случае это не единственный способ испортить строку без ключевого слова unsafe . Просто вызовите функцию, которая топает строку. Он получает указатель на содержимое строки в куче GC или в куче загрузчика (интернированные строки), копия не создается. Это также не поддающийся проверке код и такой же неиспользуемый при работе в песочнице.

Суть дела: ключевое слово C # unsafe вовсе не связано с тем, что CLR считает проверяемым, и, следовательно, фактически безопасным кодом. Он заботится о вопиющих случаях, используя указатели или пользовательские типы значений (фиксированные). Является ли это утечка в C # языка спецификации спорно. Pinvoke - более очевидный крайний случай. Привязка к функции операционной системы довольно опасна. Пинковать какую-то стороннюю библиотеку С нет.

Но я должен согласиться с @fej, [FieldOffset] должен был пройти курс лечения "ты уверен". Жаль, что для этого нет синтаксиса. По общему признанию, я еще не выяснил, почему это действительно должно влиять на управляемый макет. Было бы намного больше смысла, что этот атрибут будет применяться только к маршалированной компоновке. Странно, кто-то держал туза за рукав в первые дни, может быть.

3 голосов
/ 23 декабря 2010

Мой голос за то, чтобы сделать FieldOffset небезопасным.

...