Использование делегатов в С#

В языке С# и .NET Framework вы могли бы помочь мне с пониманием делегатов? Я пытался проверить код и обнаружил, что результаты, полученные мной, были для меня неожиданными. Вот он:

class Program
{
    public static int I = 0;

    static Func<string> del = new Func<string>(I.ToString);

    static void Main(string[] args)
    {
        I = 10;
        Console.WriteLine("{0}", del());
    }
}

Ответ был 0, но не 10. Почему?

Ответ 1

Причина в следующем:

Как вы объявляете делегата, он указывает непосредственно на метод ToString для статического экземпляра int. Он захватывается во время создания.

Как отмечает Флиндеберг в комментариях ниже, у каждого делегата есть цель и метод, который должен быть выполнен на цели.

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

Позже вы меняете I на другое значение, в основном присваивая ему новый экземпляр. Это не волшебным образом изменяет экземпляр, захваченный в вашем делегате, почему это должно быть?

Чтобы получить ожидаемый результат, вам нужно будет изменить делегат:

static Func<string> del = new Func<string>(() => I.ToString());

Подобным образом делегат указывает на анонимный метод, который выполняет ToString в текущем I во время выполнения делегата.

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

Посмотрите на код, который компилятор создает для второй версии делегата:

private static Func<string> del = new Func<string>(UserQuery.<.cctor>b__0);
private static string cctor>b__0()
{
    return UserQuery.I.ToString();
}

Как вы можете видеть, это обычный метод, который что-то делает. В нашем случае он возвращает результат вызова ToString в текущем экземпляре I.

Ответ 2

Вам нужно передать I в свою функцию, чтобы I.ToString() мог быть выполнен в соответствующее время (а не при создании функции времени).

class Program
{
    public static int I = 0;

    static Func<int, string> del = num => num.ToString();

    static void Main(string[] args)
    {
        I = 10;
        Console.WriteLine("{0}", del(I));
    }
}

Ответ 3

Вот как это должно быть сделано:

using System;

namespace ConsoleApplication1
{

    class Program
    {
        public static int I = 0;

        static Func<string> del = new Func<string>(() => {
            return I.ToString();
        });

        static void Main(string[] args)
        {
            I = 10;
            Console.WriteLine("{0}", del());
        }
    }
}

Ответ 4

Разрешить делегат С# инкапсулировать как объект, так и экземпляр и метод. Объявление делегата определяет класс, полученный из класса System.Delegate. Экземпляр делегата инкапсулирует список вызовов, который представляет собой один или несколько методов списка, каждый из которых называется вызываемым объектом.

узнать больше формы

http://asp-net-by-parijat.blogspot.in/2015/08/what-is-delegates-in-c-how-to-declare.html

Ответ 5

Мое предположение заключается в том, что int передаются значениями не ссылок, и по этой причине при создании делегата он является делегатом метода ToString текущего значения "I" (0).