Как я могу отправить вызов делегату, тип которого не завершен на момент отправки?

У меня проблемы с отправкой вызова делегату, тип которого не завершен на момент отправки. Я уточню: я объявил следующий тип делегата:

// Delegate type. The 'firstArgument' will be 'this', i.e., this is an open
// instance method: the implicit argument is here given explicitly, in
// 'firstArgument'. (See link below for explanation on open instance delegates).
public delegate Object DirectReadAccessor<T>(T firstArgument);

И теперь я пытаюсь динамически (то есть с помощью TypeBuilder) создать следующий класс:

public MyClass {

    // Array of delegates. T has been replaced with MyClass because the
    // argument will be 'this', which is of type MyClass.
    private static DirectReadAccessor<MyClass>[] directReadAccessors;

    // Method that looks up a delegate in the array of delegates and calls it
    // with 'this'.
    public Object DirectRead(int i) {
        directReadAccessors[i](this);
    }

    // Method that is called by the declaring type to pass an array with the
    // MethodInfo of some methods. MyClass then creates delegates for these
    // methods and stores them in the directReadAccessors array.
    public static void InitializeClass(MethodInfo[] directReadAccessorsMInfo) {
        int length = directReadAccessorsMInfo.Length;
        Type[] typeArguments = new Type[] { typeof(MyClass) };
        directReadAccessors = new DirectReadAccessor<MyClass>[length];
        // For each method in directReadAccessorsMInfo...
        for (int i = 0; i < length; i++) {
            // Create a delegate and store it in directReadAccessors.
            directReadAccessors[i] = (DirectReadAccessor<MyClass>)
                   Delegate.CreateDelegate(
                          DirectReadAccessor<MyClass>, // Type of the delegate.
                          null, // Specify null first argument so that it's
                                // *open* instance.
                          directReadAccessorsMInfo[i].MakeGenericMethod(typeArguments) // The method.
                    );
        }
    }

}

* в делегатах открытого экземпляра.

Это было сложно, потому что MyClass не существует, когда я пытаюсь объявить поле directReadAccessors, которое имеет тип DirectReadAccessor [], или когда я испускаю метод InitalizeClass, который снова использует MyClass, которого еще не существует (это что я создаю). Однако мне удалось все это сделать, но теперь у меня проблемы с методом DirectRead, так как я не знаю, как вызвать делегат, когда он у меня есть в стеке. По-видимому, мне нужно следующее излучение:

ilGenerator.Emit(OpCodes.Callvirt, invokeMInfo);

где invokeMInfo - это метод Invoke для DirectReadAccessor, который я должен получить следующим образом:

MethodInfo invokeMInfo = typeof(DirectReadAccessor<MyClass>).GetMethod(
        "Invoke",                        // Name of the method.
        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, // Binding attributes.
        null,                            // Binder.
        new Type[] { typeof(MyClass) },  // Types of the arguments.
        null                             // Modifiers for the arguments.
);

Опять же, проблема в том, что ни MyClass, ни DirectReadAccessor еще не существуют. У меня есть TypeBuilder для MyClass и незавершенный тип DirectReadAccessor, которые я создал следующим образом:

directReadAccessorType = typeof(DirectReadAccessor<>).MakeGenericType(typeBuilder);

Но если я попытаюсь вызвать GetMethod ("Invoke", ....) для directReadAccessorType, как показано выше, я получу NotSupportedException, потому что я не могу получить метод Invoke для незавершенного типа. Я проверил это предположение, выполнив тот же вызов после завершения типа с помощью:

typeBuilder.CreateType();

И действительно, в этом случае я не получаю исключения. Однако мне нужно иметь возможность получить MethodInfo метода Invoke до завершения типа, пока я испускаю код для InitializeClass.

Это странная ситуация: у меня будет делегат, когда он мне понадобится, но я не могу создать код для его вызова. Может ли кто-нибудь помочь?

Большое спасибо и извините за пространный пост.


person Alix    schedule 03.03.2010    source источник
comment
Без обид, но это только я, или разработчики пишут слишком сложный код?!?   -  person Mitch Wheat    schedule 03.03.2010
comment
Ни одного. Это 5-месячный проект, который пытается перенести на C # уже существующую систему, которая с самого начала была довольно сложной. На самом деле то, что я пытаюсь сделать здесь, не так уж и сложно, у меня просто есть массив делегатов, и я хочу иметь возможность их вызывать, вот и все. Единственная проблема заключается в том, что я использую динамически создаваемый тип, но если вам не разрешено использовать такие типы, то какой смысл предлагать эту функциональность в первую очередь?   -  person Alix    schedule 03.03.2010


Ответы (3)


Это сложная проблема. Я думаю, вы должны иметь возможность использовать TypeBuilder.GetMethod, чтобы получить MethodInfo, который вам нужен, используя вызов по строкам

TypeBuilder.GetMethod(directReadAccessorType, 
    typeof(DirectReadAccessor<>).GetMethod("Invoke"));
person kvb    schedule 03.03.2010
comment
Боже мой, это именно то, что вам нужно. Оно работает! Спасибо, так так, так так, так, так что я борюсь с этим в течение нескольких дней! (это очень расстраивало; у меня проблемы с поиском нужных мне библиотечных вызовов, вроде этой). Еще раз спасибо, вы действительно сделали мой день! :) - person Alix; 04.03.2010
comment
@Alix - Я рад, что смог помочь. К сожалению, статические методы GetMethod, GetField и GetConstructor на TypeBuilder не очень легко обнаружить, поэтому легко застрять при выполнении сложных задач с помощью Reflection.Emit и дженериков. - person kvb; 04.03.2010
comment
Расскажи мне об этом :). Я занимаюсь программированием в течение многих лет (хотя и не на C #), и обычно мне не нужно много спрашивать на форумах, но с тех пор, как я начал работать с C # пару недель назад, мне пришлось задать немало вопросов. Конечно, кое-что из того, что я пытаюсь делать, довольно необычно :) - person Alix; 04.03.2010

Вы пробовали скомпилировать общий код и посмотреть на него с помощью ILDASM? Он должен показать вам правильный IL и, надеюсь, правильный выброс для генерации.

person Timores    schedule 03.03.2010
comment
Привет. Да, там, где я говорю. По-видимому, мне нужно следующее излучение выше, это излучение было получено, как вы предлагаете. Проблема в том, что я думал, что могу просто загрузить делегат в стек во время выполнения (что я делаю) и вызвать его как есть, без необходимости предоставлять метод Invoke, специфичный для типа делегата, когда я испускаю код (поскольку тип не пока не существует). - person Alix; 03.03.2010

Имеет ли смысл минимизировать количество кода в сгенерированном классе?

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

person Timores    schedule 03.03.2010
comment
Привет. Проблема в том, что это система, которая принимает классы, написанные пользователями (в данном случае программистами приложений), и расширяет их некоторыми функциями, и, прежде всего, обеспечивает безопасный доступ к полям (через средства доступа, делегаты которых у меня есть в массиве directReadAccessors). Все это должно выполняться автоматически, и я не хочу заставлять пользователей писать интерфейсы и т. Д. Я должен делать все это, динамически генерируя классы. Если у вас есть идеи, как это сделать лучше, дайте мне знать. Спасибо! - person Alix; 03.03.2010
comment
Я не уверен, что понял ваши требования (кажется, что программисты приложений должны иметь возможность реализовать интерфейс с помощью одного метода), но другим способом решения проблемы может быть создание C # и его компиляция, вызвав csc, а затем загрузив полученную DLL. . Или предоставление шага сборки или сценария, который делает это, чтобы программисты приложений ссылались на эту сгенерированную DLL. - person Timores; 03.03.2010
comment
это система, которая принимает классы, написанные пользователями - дрожит - person Mitch Wheat; 04.03.2010
comment
Я уверен, что они смогут это реализовать, я просто не хочу, чтобы они меняли свой код больше, чем мне нужно. Но kvb уже решил мою проблему, так что не беспокойтесь! Спасибо за ваши предложения! - person Alix; 04.03.2010