tags:

views:

332

answers:

4

Edit: Warning - I now realize that the following technique is generally regarded as a bad idea because it creates hidden dependencies for the sake of looking neat.


I recently discovered that you can use the StackTrace to infer information about the caller of a method.

This enables you to create a seemingly "cool" API whereby you simply invoke a method without bothering to pass any explicit parameters to it, and the method works out what to do based on the StackTrace.

Is this a bad thing to do, and if so, why?

Example:

public class Cache {
  public Object CheckCache()
  {
    Object valueToReturn = null;
    string key = GenerateCacheKeyFromMethodPrototype(new StackTrace().GetFrame(1).GetMethod()); //frame 1 contains caller
    if(key is in cache) valueToReturn = itemFromCache;

    return valueToReturn;   
  }
}

public class Foo { 
  private static Cache cache = new Cache();

  public Blah MethodFoo(param1, param2...)
  {
    Blah valueToReturn = cache.CheckCache(); //seems cool!
    if(valueToReturn == null)
    {
      valueToReturn = result of some calculation;
      //populate cache
    }

    return valueToReturn;
  }
}

I'm sure there are errors in the above pseudocode, but you get my drift.


Edit: thanks for everyone's responses.

+5  A: 

There's two reasons why not to do this:

  • It's slow
  • It's creates a brittle solution.

If you wanted to do this, you'd better off using a tool that supports Aspect Oriented Programming, such as Castle's Dynamic Proxy.

David Kemp
thanks for your answer - please can you expand upon why this is brittle?
Ben Aston
What happens when the calling method is removed? In your example it doesn't matter so much, but in other more real-world cases it may matter.Also, what happens when there are overloaded methods? Will this technique of inspecting the stack to decide what to do still function appropriately?
Mike Chess
+4  A: 

Another problem is that the compiler may "inline" your method in the optimisation process, e.g.

void MethodA() {
    MethodB();
}

void MethodB() {
   foo();
}

becomes:

void MethodA() {
   foo();
}

This is obviously a problem because the immediately caller for foo is nolonger MethodB, but instead MethodA. There is an attribute you can place on your method to prevent it being inlined though:

[MethodImpl( ... NoInline )]

(I can't remember the exact parameter)

-Oisin

x0n
[MethodImpl(MethodImplOptions.NoInlining)] is the parameter.Though as other have said, this should be a last option. I've only found one half valid use for this in oh so many years of coding.
Chuck
+2  A: 

It's bad because I can't know what the function does when I call it. I can't test the function, because my test will call it from a different function, which may invoke different behavior.

And it's bad because there's now an "invisible" contract I must obey when calling the function. Not only do I have to ensure that the correct parameters are passed, I also have to ensure that the current function has the correct name. What if I were to wrap the function call in an anonymous lambda function? Suddenly I've changed the behavior of the program, and I had not guessed that in advance, so now I can spend the next day debugging why the program suddenly and magically broke.

What happens with overloaded functions? Do you distinguish between them? Functions with the same name, but in different classes? Functions with "special" names, like constructor, finalizer, operators or lambdas?

And what about when functions are inlined by the compiler?

jalf
+1  A: 

That doesn't seem cool to me at all. Why would you want to put every possible way to get required state in the method itself? This is like an anti pattern for dependency injection. Don't go there.

Brian Rasmussen
ok, thanks for your response. I can now see that this approach brings with it a whole load of issues.
Ben Aston