2

Lets say I have 3 classes:

class A {
   void do_A() {
      //Check object call hierarchy
    }
}
class B {
   void do_B() {
      A a;
      a.do_A();
    }
}
class C {
   void do_C() {
      B b;
      b.do_A();
    }
}

And then I call:

C c;
c.do_C();

How can i get the object call hierarchy from within A's do_A() ?

I mean I want to get the references of object in a.do_A() (can be easily attained by this), the reference of object b that called a.do_A(), and the reference of object c that called b.do_B().

I think this should be possible, because I can get the call hierarchy with call stack, so I'm sure I should be able to get some more information about the objects who called the methods.

Jeff Atwood
  • 63,320
  • 48
  • 150
  • 153
Yochai Timmer
  • 48,127
  • 24
  • 147
  • 185
  • what do you mean by getting the reference? your methods are void so nothing is returned. What do you need that is not already available by reading stackTrace and stackFrames ? – Davide Piras Jul 05 '11 at 14:04
  • 1
    Unfortunatelly, this is not possible. You can get the methods, but not the instances. – Daniel Hilgarth Jul 05 '11 at 14:06
  • you can get the type of the object per StackFrame , but that won't give you a reference to it. (Well, at least i couldn't find it in documentation) – Yochai Timmer Jul 05 '11 at 14:10
  • @dlev - it's easy to get the static information (type, method name, etc..) but I'm looking for the runtime reference of the actual object that called it. – Yochai Timmer Jul 05 '11 at 14:13

3 Answers3

12

First off, what you are describing is a bad programming practice. A method's behaviour should depend upon its argument, not upon who called it. A method should be reliable, so that you know that you get the same behaviour no matter who called it.

Second, you seem to be of the common but false belief that the call stack is a real thing that tells you where the call came from. That is not at all the purpose of the call stack, though for debugging scenarios it sure is useful. The purpose of a call stack is to tell you where you are going next, not where you came from. Now, it is usually the case that where you are going next is also where you came from. The fact that often you can deduce where you came from based on knowing where you are going next is frequently useful, but you cannot rely upon it. The call stack is permitted to contain only enough information to determine where you are going next; it can throw away every bit of information not necessary to determine where you are going next.

Third, you seem to be of the common but false belief that call stacks are the only system for determining where you are going next, and hence, where you came from. They are not. As we'll see in the next version of C# and VB, asynchronous control flows utterly divorce "where you came from" from "where you are going next" and do not use call stacks at all.

Perhaps you can tell us why you want this information. There is probably a better way to do what you want.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • First point, agree. Second point- the CallStack is exactly there to store where you came from. That's why it's stored, and that's why it's saved as a stack. Third - Each thread has its own call stack, because the flow is different and started from a different point. Asynchronous calls won't change this fact, it will merely hide it better. – Yochai Timmer Jul 05 '11 at 15:29
  • And no reason, was just curious. – Yochai Timmer Jul 05 '11 at 15:30
  • 2
    @Yochai: I assure you, the call stack is *not* there to tell you where you came from. It is to tell you where you are going next. Consider for example a tail recursive method. Also, you seem to be of the belief that multithreading and asynchrony are somehow related. The point of asynchrony is to *eliminate* multithreading. Regardless, the number of calls stacks per thread is irrelevant. – Eric Lippert Jul 05 '11 at 15:32
  • Another example where the call stack behaves strangely is method inlining. There the inlined function doesn't appear on the call stack, because it *technically* wasn't called anymore. And the tail call scenario doesn't only apply to to tail recursion, but to any tail call, which makes the call stack even more confusing. – CodesInChaos Jul 05 '11 at 20:57
  • @Eric: Can you post a link or something that explains the stack as you propose ? (it kinda contradicts the way i know it works in other languages (c++, assembly)). It could be interesting to learn another perspective. – Yochai Timmer Jul 06 '11 at 09:02
  • Thanks, I've asked that question and got a sufficient answer, interesting. :) http://stackoverflow.com/questions/6595473/callstack-determines-where-you-are-going-next – Yochai Timmer Jul 06 '11 at 12:54
4

I mean I want to get the references of object in a.do_A() (can be easily attained by this), the reference of object b that called a.do_A(), and the reference of object c that called b.do_B().

I think this should be possible, because I can get the call hierarchy with call stack, so I'm sure I should be able to get some more information about the objects who called the methods.

In general, what you ask for is not possible in .NET - even in theory. Perhaps unintuitively, there's no guarantee that an object is still alive even when an instance-method on that object is in the midst of execution. Essentially, the CLR is smart enough to recognize when the hidden this reference passed to an instance-method will no longer be dereferenced. The referenced object can then become eligible for collection when this happens (assuming it is not reachable through other roots, of course).

As a corollary, it's also perfectly possible for a "calling object" to be dead while the method it has called is still executing. In your specific example, it's perfectly possible that b and c (really the objects referred to by those variables) don't exist anymore while A.do_A() is executing.

What this means of course is that the information you seek may no longer be available in any form in the process, and no "magic" API should be able to reliably produce it.

I recommend reading Raymond Chen's article: When does an object become available for garbage collection? to understand this issue better:

An object can become eligible for collection during execution of a method on that very object.

If you feel this doesn't relate to your question, consider the second-last paragraph in that article:

Another customer asked, "Is there a way to get a reference to the instance being called for each frame in the stack? (Static methods excepted, of course.)" A different customer asked roughly the same question, but in a different context: "I want my method to walk up the stack, and if its caller is OtherClass.Foo, I want to get the this object for OtherClass.Foo so I can query additional properties from it." You now know enough to answer these questions yourself.

Ani
  • 111,048
  • 26
  • 262
  • 307
  • Ok, but till it's not guaranteed that its been freed, so why can't I get the information ? The "this" information should still be stored somewhere so when I get back from the method it can be used. And if it's freed I'm sure I can get that information too. (weak pointers can check if the object is still there) – Yochai Timmer Jul 05 '11 at 14:30
1

That is not possible, if A want knowledge of who calls do_A, then do_A must define a parameter object caller and its the caller responsibility to pass the correct object instance to the parameter.

João Angelo
  • 56,552
  • 12
  • 145
  • 147
  • Any concrete reason why i couldn't get that information from the program's StackTrace and actual stack ? The "this" information is probably stored somewhere along the way for each object we are using. And it's retrieved when we come back from the methods to the object's scope. – Yochai Timmer Jul 05 '11 at 14:17
  • 2
    To be honest, the best reason I can find is that it doesn't make much sense and if you require this then there is probably another way to design the system in a way that doesn't requires this hackery. – João Angelo Jul 05 '11 at 14:22