Страницы

Поиск по вопросам

вторник, 10 декабря 2019 г.

Можно ли неявно получить ссылку на класс из которого был вызван конструктор другого класса?

#c_sharp #net


Допустим, есть класс, который вызывает в методе конструктор и создает тип.

Можно ли неявно в конструкторе класса получить ссылку на класс без явной передачи
this в конструктор?

Пример:

 public class A
 {
     public void MethodA()
     {
         var b = new B();
     }
 }

 public class B
 {
     public B()
     {
         // каким-то образом неявно получаем ссылку на класс, который вызвал конструктор. 
         // Т.е в данном случае ссылка на экземпляр A
     }
 }


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

P.S Задач никаких нету. Интереса ради.
    


Ответы

Ответ 1



Нет, вы не можете получить экземпляр вызывающего класса. Этой информации нету даже на уровне IL. Конструктор B, декомпилированный в ILDasm, выглядит так: .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Размер кода: 9 (0x9) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: nop IL_0007: nop IL_0008: ret } // end of method B::.ctor У него нету объявления параметров, так что скрытых параметров нету. То, что вы можете выяснить — это какой конкретно метод вас вызывает, без указания на экземпляр класса. Это делается так: [MethodImpl(MethodImplOptions.NoInlining)] public B() { MethodBase callingMethod = new StackFrame(1).GetMethod(); Console.WriteLine($"Called from type: {callingMethod.DeclaringType.FullName}, " + $"calling method name: {callingMethod.Name}"); } У меня выводит: Called from type: Test.A, calling method name: MethodA Вы создаёте stack frame, начинающийся на 1 выше вашего текущего фрейма, и запрашиваете метод. Имея reflection-дескриптор метода, вы можете получить информацию из него. Заметьте, что я применил атрибут MethodImplOptions.NoInlining, чтобы запретить встраивать этот метод в точку вызова, в противном случае в stack trace могла бы попасть не та информация. Ещё один немаловажный момент: запрос StackFrame — затратная, дорогостоящая операция, поэтому не стоит применять это решение в production-коде. Если вы хотите информацию о том. кто вас вызвал, в production-коде, стоит доверить это компилятору и воспользоваться атрибутом [CallerMemberName], доступным начиная с .NET 4.5: public B([CallerMemberName] string callerName = null, [CallerFilePath] string callerFile = null, [CallerLineNumber] int callerLineNumber = -1) { Console.WriteLine($"Called from method: {callerName}, " + $"located {callerFile}@{callerLineNumber}"); } Выводит: Called from method: MethodA, located D:\full path here\Test\Program.cs@42

Комментариев нет:

Отправить комментарий