MSIL - как вы вызываете закрытый метод из MSIL? - PullRequest
6 голосов
/ 27 октября 2010

Я пишу «фабрику слабых событий» - код, который преобразует любого Делегата в нового делегата с идентичной подписью, но с реализацией WeakReference на цели.Я использую MSIL, чтобы избежать вызовов Delegate.CreateDelegate (производительность которого оказалась медленной).

Делегаты со слабыми ссылками отлично работают , пока базовый метод ( Метод исходного делегата), был объявлен публичным.Как только частный или анонимный метод используется, бомбы MSIL во время выполнения с MethodAccessException .

Используя скомпилированные деревья выражений, я смог вызвать частные методы, поэтому ондолжна быть возможность динамически испускать MSIL, который вызывает закрытый метод.... так что не так со следующим?

        // var target = this.Target
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Callvirt, targetPropGetter);         
        il.Emit(OpCodes.Stloc, ilTarget);            

        // if(target != null)
        // {
        il.Emit(OpCodes.Ldloc, ilTarget);
        il.Emit(OpCodes.Brfalse_S, ilIsNullLabel);

        //      Method( @target, parm1, parm2 ...);
        il.Emit(OpCodes.Ldloc, ilTarget);                  // this = Target
        short argIndex = 1;
        foreach (var parm in delgParams)                   // push all other args
            il.Emit(OpCodes.Ldarg, argIndex++);

        il.Emit(OpCodes.Callvirt, delegat.Method);   // <-- Bombs if method is private
        il.Emit(OpCodes.Ret);

        // }
        il.MarkLabel(ilIsNullLabel);

Так в чем же секрет вызова частного участника?Рефлексия может это сделать, деревья выражений могут сделать это ... почему вышеприведенный код не работает?


РЕДАКТИРОВАТЬ: Большое спасибо всем, кто предоставил ответы здесь.Оказывается, что единственным решением, которое последовательно работало в моем контексте, было использование универсальных делегатов (Action) ..., поскольку Action происходит из mscorlib, JIT, кажется, совершенно счастлив позволить it вызывать закрытый метод.попробуйте использовать свой собственный делегат, и JIT будет работать так же, как если бы вы посылали вызов или Callvirt непосредственно на цель.

Любой, кому интересно посмотреть рабочий код, может перейти к codeplex - ответы, приведенные здесь, помогли реализовать возможности WeakDelegate.

Ответы [ 3 ]

5 голосов
/ 27 октября 2010

Вы вставляете свой IL в DynamicMethod или в метод в динамической сборке? Насколько я понимаю, нет способа пропустить проверки видимости из динамической сборки, но вы можете пропустить их при использовании DynamicMethod (см. здесь ).

5 голосов
/ 28 октября 2010

Решением (для моей конкретной проблемы) было использование делегатов вместо прямых вызовов методов.Вы можете с комфортом создать открытый делегат и передать его в код IL, а затем, когда код IL вызывает метод Invoke делегата, JIT принимает шаблон как допустимый и разрешает вызов закрытых методов.

LikeЯ сказал, что это решение (которое, к счастью, позволяет сгенерированному во время выполнения коду вызывать закрытые методы), хотя оно все еще не объясняет, как технологии, такие как деревья выражений и рефлексия, могут вызывать закрытые методы.

0 голосов
/ 27 октября 2010

Используйте Call, а не Callvirt.

[Редактировать: Не в качестве общей рекомендации, а специально для решения этой проблемы]

Callvirt предназначен для вызова виртуальных методов, где адрес назначения также зависит от точного типа экземпляра. Это не работает, когда вы используете слабую ссылку.

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

...