Причина в следующем:
То, как вы объявляете делегат, указывает непосредственно на ToStringметод статического экземпляра int. Он зафиксирован во время создания.
Как указывает Флиндеберг в комментариях ниже, у каждого делегата есть цель и метод, который должен выполняться на цели.
В этом случае очевидно, что выполняемый ToStringметод - это метод. Интересной частью является экземпляр, на котором выполняется метод: это экземпляр Iво время создания, а это означает, что делегат не использует Iэкземпляр для использования, но сохраняет ссылку на сам экземпляр.
Позже вы измените Iзначение, присвоив ему новый экземпляр. Это не меняет волшебным образом экземпляр, захваченный в вашем делегате, зачем это нужно?
Чтобы получить ожидаемый результат, вам нужно будет изменить делегата на это:
static Func<string> del = new Func<string>(() => I.ToString());
Таким образом, делегат указывает на анонимный метод, который выполняется ToStringв текущем состоянии Iво время выполнения делегата.
В этом случае выполняемый метод - это анонимный метод, созданный в классе, в котором объявлен делегат. Экземпляр имеет значение null, поскольку это статический метод.
Взгляните на код, который компилятор генерирует для второй версии делегата:
private static Func<string> del = new Func<string>(UserQuery.<.cctor>b__0);
private static string cctor>b__0()
{
return UserQuery.I.ToString();
}
Как видите, это нормальный метод, который что- то делает . В нашем случае он возвращает результат вызова ToStringтекущего экземпляра I.