Как НА САМОМ ДЕЛЕ работает func

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

Но что на самом деле происходит, когда вы возвращаете функцию?

Как ваше приложение узнает, какой экземпляр объекта использовать с этой функцией?

Мой инстинкт подсказывает мне, что ссылка на экземпляр объекта передается вместе с функцией, но так ли это на самом деле?

Кажется, я не могу найти много по этой теме.

Изменить. Чтобы уточнить, я спрашиваю, когда метод возвращает функцию.


person Anthony Russell    schedule 12.12.2013    source источник
comment
Вы спрашиваете, что происходит, когда вы возвращаетесь из метода? Или что происходит, когда вы создаете анонимный метод (например, тот, который вы назначаете Func<T>)?   -  person Cory Nelson    schedule 13.12.2013
comment
Можете ли вы уточнить, я не совсем уверен, интересно ли вам, как С# передает объекты (поскольку вы говорите о передаче указателя), или вам интересно, как замыкания работают с типами делегатов   -  person Darren Kopp    schedule 13.12.2013
comment
Возможный дубликат stackoverflow.com/questions/12808673/   -  person Reacher Gilt    schedule 13.12.2013
comment
@CoryNelson Извините, я имел в виду, когда вы возвращаете функцию из метода   -  person Anthony Russell    schedule 13.12.2013


Ответы (5)


Func — это делегат — тип объекта, связанный с определенной сигнатурой метода и обычно привязанный к определенному методу с этой сигнатурой. Это может быть либо static, либо метод экземпляра, и он может быть общим или нет; во всех случаях экземпляр делегата содержит всю информацию, необходимую для вызова метода.

person Jon    schedule 12.12.2013
comment
Значит, он также включает информацию об экземпляре? Например, что, если бы были переменные экземпляра, на которые вам нужно было ссылаться в переданной функции? - person Anthony Russell; 13.12.2013
comment
@AMR: Вы имеете в виду переменные, захваченные замыканием? - person Jon; 13.12.2013
comment
@AMR: в таких случаях компилятор создает вложенный класс и делает как код, так и переменные, которые вы собираете, членами этого класса. Затем делегат ссылается на метод и экземпляр класса, как всегда (например, MethodInfo + указатель на экземпляр). Важно помнить, что то, что делает компилятор, чтобы предоставить вам это удобство, не связано с тем, как работает Func. Если вы хотите, вы можете смоделировать закрытие самостоятельно (но, конечно, никто этого не делает, потому что это было бы мазохистично). - person Jon; 13.12.2013
comment
вы получаете приз. Спасибо, что удовлетворили мое любопытство - person Anthony Russell; 13.12.2013

Func — это делегат, а экземпляры Delegate инкапсулируют MethodInfo и необязательный целевой объект для вызывать метод при вызове.

person Lee    schedule 12.12.2013

Func<T> просто делегат. Если для него задан метод экземпляра, то он [волшебным образом] получает правильное значение this при вызове. Если задан статический метод, то this нет. И если для него установлено замыкание/лямбда, замыкание «захватывает» эти переменные в области видимости во время закрытия. лексическая область видимости (с некоторыми оговорками: не всегда все работает так, как вы думаете).

Отредактировано, чтобы добавить:

Из стандарта C#, ISO 23270:2006 Языки программирования информационных технологий C#, 8.10:

8.10 Делегаты

Делегаты включают сценарии, к которым некоторые другие языки обращаются с помощью указателей на функции. Однако, в отличие от указателей на функции, делегаты являются объектно-ориентированными и типобезопасными.

Объявление делегата определяет класс, производный от класса System.Delegate. Экземпляр делегата инкапсулирует один или несколько методов, каждый из которых называется вызываемым объектом. Например, методы экземпляра, вызываемый объект состоит из экземпляра и метода для этого экземпляра. Для статических методов вызываемый объект состоит только из метода. Имея экземпляр делегата и соответствующий набор аргументов, можно вызывать все методы этого экземпляра делегата с этим набором аргументов.

Более подробно, в другом стандарте, ISO 23271:2006, Общеязыковая инфраструктура информационных технологий (CLI), разделы I–VI, говорится в 14.6:

Конструктор экземпляра (с именем .ctor и помеченным specialname и rtspecialname, см. §10.5.1) должен принимать ровно два параметра, первый из которых имеет тип System.Object, а второй — тип System.IntPtr. При фактическом вызове (через инструкцию newobj, см. Раздел III) первый аргумент должен быть экземпляром класса (или одного из его производных классов), который определяет целевой метод, а второй аргумент должен быть указателем на метод. быть вызванным.

Это не эта магия.

person Nicholas Carey    schedule 12.12.2013
comment
@AMR, подробности см. в моем исправленном ответе. - person Nicholas Carey; 13.12.2013

Когда вы создаете анонимный метод, например, назначенный Func<T>, локальные переменные, используемые внутри него, переносятся в класс. Учти это:

int GetIncremented()
{
    int x = 0;

    Func<int> increment = () => ++x;
    increment();

    return x;
}

За кулисами будет создано что-то вроде этого:

sealed class FuncData
{
    public int x = 0;

    public int FuncIncrement(FuncData data)
    {
        return ++data.x;
    }
}

int GetIncremented()
{
    FuncData data = new FuncData();

    Func<int> increment = new Func<int>(data.FuncIncrement);
    increment();

    return data.x;
}

Вот почему параметры out и ref нельзя использовать с анонимными методами: вы не можете хранить out или ref в качестве переменной-члена в классе.

person Cory Nelson    schedule 12.12.2013
comment
Человеку, который проголосовал против: как я могу улучшить этот ответ? Судя по ответу AMR Джону, он дает именно то, что он искал. - person Cory Nelson; 13.12.2013