Строго типизированные функции

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

Виды строго типизированных функций

Строго типизированные функции подразделяются в библиотеке на 2 группы:

  • Строго типизированные действия (TypedAction). Функции, не имеющие возвращаемого значения.

  • Строго типизированные функции (TypedFunction). Функции, имеющие возвращаемое значение.

Функции первой группы представлены классами MBSL_TypedAction. В настоящее время библиотека поддерживает типизированные действия, принимающие от 0 до 8 аргументов.

//N - Количество аргументов функции
class MBSL_TypedActionN<Class T1, ..., Class TN> : MBSL_TypedFunctionBase
{
    //Создать слабое строго типизированное действие
    //См. дальше про создание функций
    //См. дальше про слабые и сильные функции
    static MBSL_TypedActionN<Class T1, ..., Class TN> Weak(Managed instance, string function);
    //Создать сильное строго типизированное действие
    //См. дальше про создание функций
    //См. дальше про слабые и сильные функции
    static MBSL_TypedActionN<Class T1, ..., Class TN> Strong(Managed instance, string function);
    //Вызывать функцию с указанными аргументами
    void Invoke(T1 arg1, ..., TN argN);
    //Проверить эквивалентность другой функции
    //Функции эквивалентны, если они имеют один тип
    //и ссылаются на одну и ту же функцию
    //одного и того же объекта
    bool Equals(MBSL_TypedFunctionBase other);
    //Жива ли функция - см. дальше про слабые и сильные функции
    bool IsAlive();
    //Создать копию этой функции как слабую функцию.
    //См. дальше про слабые и сильные функции
    MBSL_TypedActionN<Class T1, ..., Class TN> AsWeak();
    //Создать копию этой функции как сильную функцию.
    //См. дальше про слабые и сильные функции
    MBSL_TypedActionN<Class T1, ..., Class TN> AsStrong();
}

//Особый случай функции без аргументов, аналогичен остальным
class MBSL_TypedAction0 : MBSL_TypedFunctionBase {}

//Конкретный пример - функция с 3 аргументами
class MBSL_TypedAction3<Class T1, Class T2, Class T3> : MBSL_TypedFunctionBase {}

Функции второй группы представлены классами MBSL_TypedFunction. В настоящее время библиотека поддерживает типизированные функции, принимающие от 0 до 8 аргументов.

//N - Количество аргументов функции
class MBSL_TypedFunctionN<Class ReturnType, Class T1, ..., Class TN> : MBSL_TypedFunctionBase
{
    //Создать слабую строго типизированную функцию
    //См. дальше про создание функций
    //См. дальше про слабые и сильные функции
    static MBSL_TypedFunctionN<Class ReturnType, Class T1, ..., Class TN> Weak(Managed instance, string function);
    //Создать сильную строго типизированную функцию
    //См. дальше про создание функций
    //См. дальше про слабые и сильные функции
    static MBSL_TypedFunctionN<Class ReturnType, Class T1, ..., Class TN> Strong(Managed instance, string function);
    //Вызывать функцию с указанными аргументами и получить результат
    ReturnType Invoke(T1 arg1, ..., TN argN);
    //Проверить эквивалентность другой функции
    //Функции эквивалентны, если они имеют один тип
    //и ссылаются на одну и ту же функцию
    //одного и того же объекта
    bool Equals(MBSL_TypedFunctionBase other);
    //Жива ли функция - см. дальше про слабые и сильные функции
    bool IsAlive();
    //Создать копию этой функции как слабую функцию.
    //См. дальше про слабые и сильные функции
    MBSL_TypedFunctionN<Class ReturnType, Class T1, ..., Class TN> AsWeak();
    //Создать копию этой функции как сильную функцию.
    //См. дальше про слабые и сильные функции
    MBSL_TypedFunctionN<Class ReturnType, Class T1, ..., Class TN> AsStrong();
}

//Особый случай функции без аргументов, аналогичен остальным
class MBSL_TypedFunction0<Class ReturnType> : MBSL_TypedFunctionBase {}

//Конкретный пример - функция с 3 аргументами
class MBSL_TypedFunction3<Class ReturnType, Class T1, Class T2, Class T3> : MBSL_TypedFunctionBase {}

Создание строго типизированных функций

Все виды строго типизированных функций поддерживают создание с помощью двух статических методов их класса - Weak и Strong (про различие между слабыми и сильными функциями см. дальше). Обе функции принимают два аргумента:

  • Managed instance - любой объект, наследующий Managed, которому принадлежит вызываемая функция. Не может быть null. Сторого типизированные функции поддерживают только функции экземпляров объектов.

  • string function - название функции на переданном объекте, которая должна вызываться этой строго типизированной функцией. Важно: из-за отсутствия возможности библиотека не проверяет переданное название функции на существование такой функции у объекта и на корректность типов. Этот факт лежит на ответственности создающего функцию кода. Если функция не будет существовать или типы не будут соответствовать реальным, при попытке вызова функции произойдёт ошибка.

Если переданные в функцию аргументы корректны, то будет возвращён объект функции, иначе - null.

Ниже приведён пример создания строго типизированной функции:

class Example : Managed
{
    void DoPrint(string val)
    {
        Print(val);
    }
    
    int Sum(int a, int b)
    {
        return a+b;
    }
}

Example e = new Example();
MBSL_TypedAction1<string> print = MBSL_TypedAction1<string>.Weak(e, "DoPrint");
print.Invoke("Hello, world!"); //Напечатает "Hello, world!" в логи

MBSL_TypedFunction2<int, int, int> sum = MBSL_TypedFunction2<int, int, int>.Weak(e, "Sum");
int r = sum.Invoke(3, 4); //r = 7

MBSL_TypedAction1<string> error = MBSL_TypedAction1<string>.Weak(e, "NoFunction");
error.Invoke("hello"); //Произойдёт ошибка

MBSL_TypedAction1<int> error2 = MBSL_TypedAction1<int>.Weak(e, "DoPrint");
error2.Invoke(5); //Произойдёт ошибка

Сильные и слабые функции

Все строго типизированные функции представлены в двух видах: сильные и слабые функции. Это название указывает на вид ссылки, которую держит строго типизированная функция на объект, на котором вызывается функция.

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

Слабые функции держат слабую ссылку на объект. Если больше сильных ссылок на объект нет, то он будет уничтожен, даже если функция ещё существует. Вызов такой функции приведёт к ошибке. Состояние слабой функции можно проверить с помощью метода IsAlive - он вернёт true, если объект, на который ссылается функция, всё-ещё существует и функцию можно вызвать, иначе - false.

Важно: необходимо остерегаться циклических ссылок при создании строго типизированной функции из самого объекта. В таком случае настоятельно рекомендуется создавать слабые функции и делать их сильные копии при необходимости.

class BadExample : Managed
{
    //Цилическая сильная ссылка - объект никогда не будет удалён!
    ref MBSL_TypedFunction2<int, int, int> _Sum = MBSL_TypedFunction2<int, int, int>.Strong(this, "Sum");
    int Sum(int a, int b)
    {
        return a+b;
    }
}

BadExample be = new BadExample();
MBSL_TypedFunction2<int, int, int> bs = be._Sum;

class GoodExample : Managed
{
    //Нет циклической сильной ссылки
    ref MBSL_TypedFunction2<int, int, int> _Sum = MBSL_TypedFunction2<int, int, int>.Weak(this, "Sum");
    int Sum(int a, int b)
    {
        return a+b;
    }
}

GoodExample ge = new GoodExample();
MBSL_TypedFunction2<int, int, int> gs = ge._Sum.AsStrong(); //Создаём сильную функцию, чтобы объект жил, пока жива функция.

Last updated