Вызов функций в OverScript коде из сторонней программы
Допустим, вы написали на C# или VB.NET свою библиотеку (dll) и хотите не только вызывать её функции, а и чтобы она вызывала ваши OverScript-функции. Сделать это можно двумя способами:
1. Можно создать делегат Func или Action функцией Delegate() и передать его в библиотеку.
2. Можно передать экземпляр или тип, и вызывать через рефлекшн метод Call, который у них есть.
Через делегаты
Сначала посмотрим, как создаются делегаты.
object d=Delegate(Test(int\, long\), "System.Func`3[System.Int32,System.Int64,System.String]");
WriteLine(d.Raise(567, 123L)); //a=567; b=123;
ReadKey();
string Test(int a, long b){
return $"a={a}; b={b};";
}
Второй аргумент в Delegate() - это строковое представление типа Func<int, long, string> в .NET. Проверим в C#:
Console.WriteLine(typeof(Func<int, long, string>)); //System.Func`3[System.Int32,System.Int64,System.String]
Тип возвращаемого значения указывается последним.
Создадим такую DLL (C#):
using System;
public class SimpleTest
{
public static void SomeLibFunc(Func<int, long, string> delegateByScript)
{
Console.WriteLine("Calling from lib: " + delegateByScript(10, 5L));
}
}
Скомпилируем код в SimpleTestLib.dll и положим его в папку с OverScript-скриптом.
Теперь вызовем функцию в библиотеке с передачей её делегата.
const object Lib=typeof("SimpleTestLib.dll", "SimpleTest"); //typeof умеет загружать тип из файла (указывается первым)
object d=Delegate(Test(int\, long\), "System.Func`3[System.Int32,System.Int64,System.String]");
Lib->SomeLibFunc(d);
//Calling from lib: a=10; b=5;
ReadKey();
string Test(int a, long b){
return $"a={a}; b={b};";
}
Работает! Функция SomeLibFunc в DLL вызвала функцию Test в скрипте.
Добавлю, что лучше создавать делегат так:
const object FuncType=typeof("System.Func`3[System.Int32,System.Int64,System.String]");
object d=Delegate(Test(int\, long\), FuncType);
//object d=Test(int\, long\).Delegate(FuncType); //можно так, смотрится неплохо
Так тип делегата получается при загрузке скрипта (так со всеми константами), и если вы ошибётесь со строковым представлением типа, то сразу получите сообщение об ошибке, а не во время выполнения. К тому же, функции Delegate не придётся искать каждый раз тип по строке.
Создавать делегаты EventHandler и EventHandler<T> можно функцией EventHandler:
object eh=EventHandler(OnEvent(object\, object\));
eh.Raise(this); //в качестве sender-а передаём текущий инстанс, а в args, если не указывать, будет EventArgs.Empty
//sender: Instance of App; args=System.EventArgs
OnEvent(object sender, object args){
WriteLine($"sender: {sender}; args={args}");
}
Обобщенный EventHandler<T>:
object eh=EventHandler(OnEvent(object\, string\), string); //создаём EventHandler<string>
eh.Raise(this, "Hello!");
//sender: Instance of App; arg=Hello!
OnEvent(object sender, string arg){
WriteLine($"sender: {sender}; arg={arg}");
}
Вызов методом Call
DLL:
using System;
public class SimpleTest
{
public static void SomeLibFunc(object obj)
{
object v=CallInScript(obj, "Test", "Hello", Environment.TickCount); //сначала сам объект, потом имя функции, потом аргументы
Console.WriteLine("Calling from lib: "+v);
}
static object CallInScript(object obj, params object[] args)
{
return obj.GetType().InvokeMember("Call", System.Reflection.BindingFlags.InvokeMethod, null, obj, args);
}
}
Функцию CallInScript можно ещё так написать:
static object CallInScript(object obj, string funcName, params object[] args)
{
var mi = obj.GetType().GetMethod("Call");
return mi.Invoke(obj, new object[] { funcName, args });
}
OverScript:
const object Lib=typeof("SimpleTestLib.dll", "SimpleTest");
Lib->SomeLibFunc(this);
//Calling from lib: a=Hello; b=1019739031;
string Test(string a, int b){
return $"a={a}; b={b};";
}
Ещё пример:
const object Lib=typeof("SimpleTestLib.dll", "SimpleTest");
Lib->SomeLibFunc(new Foo());
//Calling from lib: a=Hello; b=1020536312;
class Foo{
public string Test(string a, int b){
return $"a={a}; b={b};";
}
}
Делать Test() публичным не обязательно.
Можно передавать не экземпляр, а тип (+ нужно ещё экзекутор передать):
const object Lib=typeof("SimpleTestLib.dll", "SimpleTest");
Lib->SomeLibFunc(Foo, Executor()); // Executor() возвращает текущий экзекутор
//Calling from lib: a=Hello; b=1020345968;
class Foo{
public static string Test(string a, int b){
return $"a={a}; b={b};";
}
}
Тогда в DLL код такой должен быть:
public static void SomeLibFunc(object obj, object exec)
{
object v = CallInScript(obj, exec, "Test", new object[] { "Hello", Environment.TickCount });
Console.WriteLine("Calling from lib: " + v);
}
Экзекутор - это объект, который выполняет код. Его нужно передавать потому, что в типе (CustomType), в отличие от экземпляра класса (ClassInstance), нет информации о том, какой экзекутор должен выполнять код (тип принадлежит скрипту, а экземпляр экзекутору).
