Подтвердить что ты не робот

Почему я не могу изменить значение String.Empty?

Хотя я понимаю, что изменение значения String.Empty было бы плохой идеей, я не понимаю, почему я не могу этого сделать.

Чтобы понять, что я имею в виду, рассмотрим следующий класс:

public class SomeContext 
{ 
    static SomeContext(){}
    public static readonly string Green = "Green";
    public static readonly SomeContext Instance = new SomeContext();

    private SomeContext(){}
    public readonly string Blue = "Blue";

    public static void PrintValues()
    { 
        Console.WriteLine(new { Green, Instance.Blue, String.Empty }.ToString());
    }
}

У меня есть небольшое консольное приложение, которое пытается манипулировать этими тремя полями только для чтения. Он может успешно превратить синий и зеленый в розовый, но Empty остается неизменным:

        SomeContext.PrintValues();
        ///  prints out : { Green = Green, Blue = Blue, Empty = }
        typeof(SomeContext).GetField("Blue").SetValue(SomeContext.Instance, "Pink");
        typeof(SomeContext).GetField("Green", BindingFlags.Public | BindingFlags.Static).SetValue(null, "Pink");
        typeof(String).GetField("Empty", BindingFlags.Public | BindingFlags.Static).SetValue(null, "Pink");
        SomeContext.PrintValues();
        ///  prints out : { Green = Pink, Blue = Pink, Empty = }

Почему?

Изначально я также спросил, почему String.Empty не является константой. Я нашел ответ на эту часть своего вопроса в другом сообщении и удалил эту часть вопроса).

Примечание. Ни один из "дубликатов" не имеет убедительного ответа на эту проблему, поэтому я задаю этот вопрос.

4b9b3361

Ответ 1

Вы не можете изменить его, потому что на вашем компьютере есть .NET 4.5. Просто измените настройку Framework Target вашего проекта на 3.5, и вы увидите, что он работает.

CLR имеет встроенное знание String.Empty. Например, вы увидите Reflector или Reference Source, что класс System.String никогда не инициализирует его. Это делается при запуске CLR. Точно, как 4.5 предотвращает видимость модификации, немного сложно сказать. Вы действительно изменили поле, просто добавьте эту строку кода:

  var s = typeof(String).GetField("Empty", 
             BindingFlags.Public | BindingFlags.Static).GetValue(null);
  Console.WriteLine(s);

И вы увидите "Розовый". Я предполагаю, что он перехвачен джиттером. Однако я не могу дать убедительных доказательств. Есть прецедент, попробуйте поработать с Decimal.MaxValue, например, другое статическое значение только для чтения. Не изменяемый в 3.5, джиттер распознает его и генерирует значение напрямую, не читая поля.

Я только что узнал, что вы поставили щедрость на еще один вопрос с тем же самым предметом. Сообщение 280Z28 довольно похоже.