FastScript .NET поддерживает следующие особенности языка C# (наиболее полно поддерживается спецификация C# 1.0, с добавлением многих особенностей более поздних версий языка):
C# 1.0:
● Классы (Classes)
● Структуры (Structs)
● Перечисления (Enums)
● Интерфейсы (Interfaces)
● События (Events)
● Перегрузка операторов (Operator overloading)
● Пользовательские операторы конверсии типов (User-defined conversion operators)
● Свойства (Properties)
● Индексные свойства (Indexers)
● Выходные параметры методов (Output parameters - out, ref)
● Массив параметров (params arrays)
● Делегаты (Delegates)
● Операторы и выражения (Operators and expressions)
● Буквальный идентификатор "@" (Verbatim identifier)
C# 2.0:
● Обобщенные типы (Generics)
● Частичные типы (Partial types)
● Nullable типы (Nullable value types)
● Разные модификаторы доступа у getter/setter (Getter/setter separate accessibility)
● Статические классы (Static classes)
C# 3.0:
● Авто-свойства (Auto-implemented properties)
● Методы расширения (Extension methods)
● Объявление переменных без указания типа (Implicitly typed local variables)
C# 4.0:
● Значения параметров по умолчанию (Optional arguments)
C# 6.0:
● Инициализаторы для авто-свойств (Auto-property initializers)
● Свойства и методы, возвращающие выражение "=>" (Expression bodied members)
● Оператор проверки на null "?." (Null propagator)
C# 7.0:
● Выходные переменные (Out variables)
● Локальные функции (Local functions)
C# 8.0:
● Модификатор "readonly" для полей (Readonly members)
● Статические локальные функции (Static local functions)
● Операторы объединения null "??" и "??=" (Null-coalescing operators)
C# 9.0:
● Инструкции верхнего уровня (Top-level statements)
Следующие особенности C# 1.0 не поддерживаются:
● Директивы препроцессора (Preprocessor directives - #if, #region, etc)
● Атрибуты (Attributes)
● Неуправляемый код (Unmanaged code: pointers, unsafe keyword, P/Invoke)
● Инструкции checked, unchecked (checked, unchecked statements)
● Инструкция goto (goto statement)
Термины, используемые ниже: "хост" означает ваше .net приложение; "скрипт" означает нечто, определенное в коде скрипта.
Классы, определенные в скрипте, можно наследовать от других скриптовых классов либо от System.Object:
class MyScriptClass: OtherScriptClass // ok
class MyScriptClass: Object // ok
class MyScriptClass // ok, same as Object
class MyScriptClass: ArrayList // error
Структура в FastScript представляет собой обычный класс. FastScript добавляет специальные методы для копирования структуры, когда ее значение передается в метод или в другую переменную. Объявление переменной, имеющей тип структуры, не создает экземпляр структуры автоматически:
MyStruct s; // s is null
s = new MyStruct(); // and must be initialized before use
Класс, определенный в скрипте, виден хосту как экземпляр FastScript.Runtime.DataContext.
Вы можете переопределять следующие методы в скриптовом классе:
● ToString
● Equals
● GetHashCode
Переопределенные методы будут также использоваться хостом.
Скриптовый класс может реализовать интерфейсы, определенные в хосте, но это будет иметь значение только в скрипте. Хост не сможет работать с экземпляром такого класса, как с реализующим интерфейс.
Nullable типы могут использовать только типы хоста.
В скрипте можно использовать только обобщенные типы и методы хоста. Вы не можете определять обобщенный тип или метод в скрипте.
Если тип хоста обозначен как "forwarded", его нужно явно использовать в хосте, чтобы была возможность его использования в скрипте. Например:
var list = new System.ComponentModel.BindingList<int>(); // error, BindingList does not exist
Если добавить следующую строку кода в ваше приложение, скрипт будет скомпилирован без ошибок:
new System.ComponentModel.BindingList<int>();
Вы можете создавать делегаты на основе любых методов (определенных в скрипте или хосте). Передача делегатов хост не поддерживается. Также не поддерживается создание делегатов Action<> и Func<>.
Конверсия типов, определенная пользователем (в коде скрипта или в хосте), ограничена теми типами, которые определены явно. Пример: если определена конверсия T->int, вы можете ее использовать. Но вы не сможете использовать конверсию T->float, если она явно не определена.
var m = new My();
int intValue = m; // ok
float floatValue = m; // error
int explicitIntValue = (int)m; // ok: explicit is not defined, but we have implicit one
float explicitFloatValue = (float)m; // error
floatValue = (int)m; // use this way
public class My
{
private object _value;
public static implicit operator int(My m) => (int)m._value;
}
В скрипте можно использовать только типы, доступные в хосте. Приложение, скомпилированное в режиме Native AOT, может не содержать некоторые типы (или члены типов), которые вы бы хотели использовать в скрипте, потому что тип/член был исключен из сборки (trimmed).
Другая проблема - использование обобщенных типов/методов. В Native AOT, вы можете использовать те обобщенные типы, которые доступны в хосте. Например, хост использует класс List<int>
, но не пользуется классом List<double>
. Первый можно будет использовать в скрипте, но при попытке создать тип List<double>
будет выдана ошибка.
Таким образом, ваша задача будет состоять в том, чтобы сделать типы (и члены типов) статически доступными в вашем приложении, чтобы их можно было использовать в скрипте. Это можно сделать различными способами (создание экземпляров типов, использование атрибутов):
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(List<>))]
public void EnsureAOTVisible()
{
var list = new List<int>();
}
Если в хосте статически доступен обобщенный тип, то поддерживается его использование с параметрами, представляющими reference type. Например, если доступен тип List<>, то можно создавать типы List<object>
, List<string>
, List<ArrayList>
и т.п.