9

В общем, задался этим вопрос из-за этого ответа по другому вопросу.

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

(Так ли это?)

Получается, что я могу взять IL-код, полученный после компиляции на C# и добавить в него какое-нибудь экзотическое поведение(Которое не запрещено в IL), которого нельзя добиться языком высокого уровня, скомпилировать и подключить эту сборку к проекту и прозрачно использовать на каком-нибудь C# ?

Собственно, интересует вопрос, на сколько все дозволено в IL и возможные побочные эффекты у пользователей таких либ.

iluxa1810
  • 24,899
  • Вообще рефлексии, IL - это её часть - дозволено всё. Только нужно знать как. Достоинство/недостаток IL в том, что под IL-код будет сгенерирована ещё одна dll. А доступ можно получить везде, не прибегая к IL – nick_n_a Nov 16 '18 at 15:03
  • Пожалуй... Используя IL можно 1)делать хорошо-оптимизированый IL код 2) можно делать "автогенерируемые" сборки. Т.е. что б не компилировать код через cs-ку, можно используя IL сделать сборку с нужными методами. (используется для генерации обмена в Entity). – nick_n_a Nov 16 '18 at 15:07
  • @nick_n_a , ну т.е в IL отсутствует инкапсуляция, а модификаторы для языков высокого уровня, что бы компилятор из них не позволял нарушать инкапсуляцию? – iluxa1810 Nov 16 '18 at 16:06
  • P.S А нет ли чтива какого, желательно на русском по IL ? – iluxa1810 Nov 16 '18 at 18:15
  • Ну вроде бы декларативная инкапсуляция точно есть: https://sharplab.io/#v2:C4LglgNgNAJiDUAfAAgZgATIEzoMIFgAoAbyPXMwzADth0ANAbjIoAcAnMANwENgBTdDToBNZoQroOAewEBjATCG10ALXEBfIA== – VladD Nov 17 '18 at 09:34
  • @VladD, ну ею можно принебрегать? Просто, если нельзя, то я не понимаю, как сгенерированный лямбда сеттер для приватного поля будет эффективнее стандартной рефлексии. – iluxa1810 Nov 17 '18 at 10:34
  • 1
    @iluxa1810: А у меня почему-то не работает: «Необработанное исключение: System.FieldAccessException: Методу "Program.Main()" не удалось получить доступ к полю "C.Y". Program.Main()» при доступе к приватному полю. – VladD Nov 18 '18 at 20:45
  • @VladD, хм... Тогда я в недоумении почему кодогенерация быстрее рефлексии. Ок, в пабликом может оно и быстрее, так как сгенерированный код сразу знает о наличии того или иного свойства. Но как быть с приватными полями? Ведь кодогенерация еще и в сериализации учавствует. В Бинарном виде, можно и приватные поля восстанавливать. – iluxa1810 Nov 18 '18 at 20:49
  • @iluxa1810: Потому что рефлексия и правда вместо прямого обращения к полю использует доступ через FieldInfo. А вот в IL-коде прямой доступ, такой же, как и на C# в случае, когда тип известен заранее. – VladD Nov 19 '18 at 17:57

3 Answers3

5

Возможности IL несколько шире возможностей C#, например можно распаковывать объекты по ссылке:

ldarg_0
unbox [mscorlib]System.Int32
ldc.i4 42
stobj [mscorlib]System.Int32
ret

Вот к чему такое приводит:

object obj = -1;
Foo(obj);
Console.WriteLine(obj); // 42

Однако, большинство ограничений C# и IL - общие: язык C#, особенно в последних версиях, позволяет очень многое из того что позволяется средой выполнения. И да, просто так обращаться к приватным полям в IL - нельзя.


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

Динамические методы могут обходить проверки видимости если у создавшего их кода достаточно привилегий (и обход проверок был включён для этого метода параметром конструктора)

VladD
  • 206,799
Pavel Mayorov
  • 58,537
  • Хм... А как в IL-коде выглядит динамический метод? У него какой-то спец. атрибут для обхода валидации или динамический метод должен быть создан обязательно в рантайме, что бы читать приватные поля? – iluxa1810 Nov 19 '18 at 07:56
  • @iluxa1810 именно так - он должен быть создан в рантайме с использованием класса DynamicMethod. Это единственное отличие. – Pavel Mayorov Nov 19 '18 at 07:58
4

В il-коде можно помечать функции как экспортируемые, которые можно будет вызывать из нативного (не дотнетного) кода. Судя по всему, сам MS эту возможность вообще не афиширует. В компиляторы C# и VB.NET они эту фичу точно не добавили, про плюсы не в курсе.

Qwertiy
  • 123,725
3

Используя IL можно выполнять CLR-команды https://en.wikipedia.org/wiki/List_of_CIL_instructions в любом порядке, каком захочется. Можно даже нарушать safe-концепцию и т п. не запрещено. Рефлексия сама по-себе позволяет получить доступ и к приватным и к другим членам класса. А IL - это язык команд. Можно создать как хорошо-оптимизированый IL-код, так и код который поломает приложение.

Но что важно, что IL-код не может существовать "сам по себе", он оформляется как сборка. Это свойство используется для генерации Entity, например. Код вида

 foreach (PropertyInfo prop in obj.Props) 
      do_something

На IL можно через цикл реализовать можно так (условно) https://subscribe.ru/archive/comp.soft.prog.csharplessons/200703/21001224.html/

 ldarg.1
 getprop a
 do_something
 ldarg.1
 getprop b
 do_something

Но нужно понимать, что для даного il (который генерируете не вы, а ваш автомат)

foreach (PropertyInfo prop in obj.Props) {
    il.Emit(OpCodes.ldarg1);
    //....
    }

нужно создать сборку, если используется динамический assembly - то будет создана временная сборка (темповый файл с dll).

О быстродействии, компиляция IL занимает время (если использовать динамическое формирование IL кода), если механизм будет использован 1 раз - ускорения не будет. Если у вас миллион вызовов по свойствам (такое часто в СУБД) - то лучше создать сборку. Потому что для доступа через привычную рефлексию (foreach, Properties, SetValue) используется в 10-20 раз больше IL-команд чем просто через IL (три команды ldarg,ldarg,setprop плюс минус). Есть либы, которое это делают автоматом.

Вообщем IL позволяет делать самомодифицирующийся(условно) код, или компилировать код неприбегая к "csc.exe".

Если цель - получить доступ к приватному свойству - то оно чаще всего доступно просто через рефлексию.

nick_n_a
  • 8,057
  • А можно немного подробнее (или ссылку, где почитать) о том, что приватные поля недоступны в системных библиотеках? – VladD Nov 17 '18 at 09:32