Есть ли в C # указатели на функции? - PullRequest
8 голосов
/ 29 апреля 2010

Я пытаюсь изучить кодирование на c # и спрашиваю, включена ли в c # концепция указателей функций. Я вижу, что есть такие вещи, как делегаты. Это одна и та же концепция? или они отличаются на более фундаментальном уровне?

Ответы [ 5 ]

17 голосов
/ 29 апреля 2010

Как уже говорили другие, вы захотите использовать делегаты для ситуаций в C #, для которых вы использовали бы указатели функций в C ++. Делегаты концептуально похожи на указатели функций, но гораздо более приятны в использовании, поскольку они инкапсулируют не только функцию, но и «принимающий» объект, который будет «этим» вызова.

Обратите внимание, что в CLR есть концепция указателей на функции. Если бы вы внимательно посмотрели на то, как компилятор C # генерирует код, который создает делегаты, вы бы увидели, что он создает управляемую ссылку на функцию и передает ее конструктору делегата. В C # нет языковой функции, которая позволяла бы получить указатель на «голую» функцию и манипулировать им напрямую.

Однако, поскольку концепция существует в CLR, теоретически возможно, что будущая версия C # будет поддерживать указатели функций как первоклассную концепцию в небезопасном коде, так же как мы поддерживаем указатели данных в небезопасном коде. В этом случае мы будем (1) отслеживать сигнатуру указателя функции в качестве типа указателя и (2) генерировать код, который использует код операции CIL «calli» (обращение через указатель).

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

Однако я бы не задержал дыхание в ожидании этой функции на твоем месте. Мы создали его прототип, и он работает достаточно хорошо, но я не думаю, что сейчас есть необходимость в его добавлении в язык общего назначения, такой как C #.

16 голосов
/ 29 апреля 2010

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

Делегаты также имеют встроенные асинхронные интерфейсы и имеют взаимную / противоположную дисперсию при назначении делегату новых функций (и, в .NET 4, при передаче делегатов вокруг)

4 голосов
/ 29 апреля 2010

Не в классическом смысле C / C ++, нет. Но концепция несколько похожа - .NET вводит концепцию делегатов для обработки ситуаций, когда вам нужна переменная для вызова метода. Делегатов нельзя "вертеть" с помощью указателей, и есть встроенная безопасность типов.

Если вы используете «правильные» указатели на функции в стиле C, концепция будет аналогичной. Но, похоже, существует много унаследованного кода, который делает забавные манипуляции с указателями, чтобы обойти безопасность типов или что-то еще.

2 голосов
/ 29 апреля 2010

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

Так что это намного больше, чем указатель на функцию.

0 голосов
/ 20 июня 2019

C # имеет нечто вроде указателя на функцию, который является делегатом вызова. Ну ... я не могу дать вам теоретический ответ на разницу между ними. Но я могу дать вам разницу в реализации кода между C # делегатом и указателями на функции C ++.

C #

delegate void voidFn();
voidFn fnDel;
List<int> intList = new List<int>();
fnDel = intList.Clear

Это легко скомпилируется в c #.

C ++

typedef void (*voidFn)();
voidFn fnDel;
std::vector<int> intList;
fnDel = intList.clear;

Нет ... Мне жаль сообщать вам, что в c ++ это не сработает, даже если логически говорить, что функция очистки вектора такая же, как void fn (). Мы не просто указываем на адрес функции вектора и говорим: «Эй! Давайте очистим этот вектор при этом обратном вызове». Я надеюсь, что кто-то может ответить на более конкретное объяснение этого, но я предполагаю, что это связано с не зная, какой вектор искать.

Однако, с небольшим полиморфизмом ... мы можем передать что-то вроде делегата C # вокруг ...

#include <iostream>
#include <vector>

class A
{
public:
    A() {}
    virtual void operator()() { std::cout << "A is called"; }
};

class B : A
{
public:
    B(std::vector<int>& vec):vec_(vec){}
    void operator()() { vec_.clear(); std::cout << "B is called" << std::endl; }

private:
    std::vector<int>& vec_;
};

int main() 
{
    std::vector<int> tmpVec;
    for (int i = 0; i < 10; ++i)
    {
        tmpVec.push_back(i);
    }
    B* b = new B(tmpVec);
    A* a = (A*)b;

    std::cout << "Current vec size: " << tmpVec.size() << std::endl;

    (*a)();

    std::cout << "Current vec size: " << tmpVec.size() << std::endl;

    delete b;

    return 0;
}

Да .. все верно ... с помощью функторов и небольшого наследования их виртуальной функции мы можем фактически иметь форму "Delegate Void VoidFn ()" в классе A. Приведенный выше код будет запускать класс B из-за того, как наследование работает, и очистит для нас 'tmpVec'. Итак, YIPPEE, мы можем написать довольно гибкий C ++ обратный вызов, который в конце концов не использует «негибкий» указатель на функцию!

...