Кто-нибудь знает, можно ли определить эквивалент "загрузчика пользовательских классов java" в .NET?
Чтобы дать небольшой фон:
Я занимаюсь разработкой нового языка программирования, который ориентирован на CLR, называемый "Свобода" . Одной из особенностей языка является его способность определять "конструкторы типов", которые являются методами, которые выполняются компилятором во время компиляции и генерируют типы как выходные данные. Это своего рода обобщение дженериков (в нем есть нормальные дженерики) и разрешить такой код (в синтаксисе "Свобода" ):
var t as tuple<i as int, j as int, k as int>;
t.i = 2;
t.j = 4;
t.k = 5;
Где "кортеж" определяется следующим образом:
public type tuple(params variables as VariableDeclaration[]) as TypeDeclaration
{
//...
}
В этом конкретном примере конструктор типа tuple
предоставляет нечто похожее на анонимные типы в VB и С#.
Однако, в отличие от анонимных типов, "кортежи" имеют имена и могут использоваться внутри публичных сигнатур методов.
Это означает, что мне нужен способ для типа, который в конечном итоге заканчивается испусканием компилятором для совместного использования в нескольких сборках. Например, я хочу
tuple<x as int>
, определенный в сборке А, чтобы в конечном итоге быть тем же типом, что и tuple<x as int>
, определенным в сборке B.
Проблема с этим, конечно же, в том, что сборка А и сборка В собираются в разное время, а это значит, что они оба выдадут свои собственные несовместимые версии типа кортежа.
Я искал использование своего рода стирания типа для этого, так что у меня была бы общая библиотека с кучей типов вроде этого (это синтаксис "Свобода" ):
class tuple<T>
{
public Field1 as T;
}
class tuple<T, R>
{
public Field2 as T;
public Field2 as R;
}
а затем просто перенаправить доступ из полей i, j и k tuple в Field1
, Field2
и Field3
.
Однако это не очень эффективный вариант. Это означало бы, что во время компиляции tuple<x as int>
и tuple<y as int>
в конечном итоге будут разными типами, тогда как во время выполнения они будут рассматриваться как один и тот же тип. Это может вызвать множество проблем для таких вещей, как идентификация равенства и типа. Это слишком непроницаемо для абстракции для моих вкусов.
Другими возможными вариантами можно было бы использовать "объекты государственной сумки". Однако использование государственного мешка может победить всю цель поддержки "конструкторов типов" на языке. Идея состоит в том, чтобы включить "пользовательские расширения языка" для генерации новых типов во время компиляции, с которыми компилятор может выполнять статический тип с помощью.
В Java это можно сделать с помощью пользовательских загрузчиков классов. В принципе, код, который использует типы кортежей, может быть выпущен без фактического определения типа на диске. Затем может быть определен пользовательский "загрузчик классов", который будет динамически генерировать тип кортежа во время выполнения. Это позволит проверять статический тип внутри компилятора и унифицировать типы кортежей через границы компиляции.
К сожалению, однако, CLR не поддерживает загрузку пользовательского класса. Вся загрузка в CLR выполняется на уровне сборки. Можно было бы определить отдельную сборку для каждого "построенного типа", но это очень быстро привело бы к проблемам с производительностью (наличие множества сборок с одним типом в них будет использовать слишком много ресурсов).
Итак, я хочу знать:
Можно ли моделировать нечто вроде Java Class Loaders в .NET, где я могу исправить ссылку на несуществующий тип, а затем динамически генерировать ссылку на этот тип во время выполнения до того, как код, который ему нужно использовать, запускается
Примечание:
* Я действительно уже знаю ответ на вопрос, который я предоставляю в качестве ответа ниже. Тем не менее, мне потребовалось около 3 дней исследований и довольно немного взлома IL, чтобы придумать решение. Я подумал, что было бы неплохо документировать его здесь, если бы кто-то другой столкнулся с той же проблемой. *