tags:

views:

3084

answers:

9

Regardless of whether or not this is a good idea, is it possible to implement an interface where the executing function is aware of the calling object's type?

class A
{
   private C;

   public int doC(int input)
   {
      return C.DoSomething(input);
   }
}

class B
{
   private C;

   public int doC(int input)
   {
      return C.DoSomething(input);
   }
}

class C
{
   public int DoSomething(int input)
   {
      if(GetType(CallingObject) == A)
      {
         return input + 1;
      }
      else if(GetType(CallingObject) == B)
      {
         return input + 2;
      } 
      else
      {
         return input + 3;
      }
   }
}

It seems to me like this is a bad coding practice (because the parameters don't change, but the output would) but aside from that is it possible?

I'm in a situation were I want a few specific types to be able to call a certain function, but I can't exclude access to the function. I thought about having a "type" parameter

DoSomething(int input, Type callingobject)

But there's no guarantee that the calling object would use GetType(this), as opposed to GetType(B) to spoof a B regardless of their own type.

Would this be as simple (relatively simple) as examining the callstack?


EDIT

Please vote up JaredPar's answer (if you want) as well as John Feminella's. I could not mark both answers as accepted, and I accepted John Feminella's answer because it is specifically what I asked for, as opposed to Jared's answer which brought up a solution which I had not previously considered.

+6  A: 

First, yes, it's a terrible idea to do this and breaks all kinds of solid design principles. You should definitely consider an alternative approach if that's open, like simply using polymorphism -- this seems like it can be refactored to a pretty clear case of single dispatch.

Secondly, yes, it's possible. Use System.Diagnostics.StackTrace to walk the stack; then get the appropriate StackFrame one level up. Then determine which method was the caller by using GetMethod() on that StackFrame. Note that building a stack trace is a potentially expensive operation, and it's possible for callers of your method to obscure where things are really coming from.


Edit: This comment from the OP makes it pretty clear that this could probably be a generic or polymorphic method. @devinb, you might want to consider making a new question that provides more detail about what you're trying to do, and we can see if it lends itself well to a good solution.

The short version is that I would end up have 30 or 40 identical functions that were simply off by one or two lines. – devinb (12 secs ago)

John Feminella
D'oh! Deleting my "duplicate answer" . . . dumb stupid slow fingers . . . +1 BTW
Binary Worrier
No sweat -- I get ninja'd all the time by that dratted Jon Skeet. BTW, I wouldn't delete your post unless it's pretty close to an exact duplicate. If you make a point that's mostly similar to mine but different in some way, definitely keep that around.
John Feminella
+1  A: 

There is (almost) always a proper design that can accomplish what you need. If you take one step back to describe what you actually need to do, I am confident you'll get at least one good design that doesn't require you to have to resort to something like this.

Rex M
My design is a very tightly coupled and self-referencing nightmare, so yes, I've come up with designs that would not have this particular ugliness, but they have ugliness of their own. I'm just trying to explore each option.
@devinb i find it highly unlikely that the other options are as ugly as this
Rex M
They're more complicated. As opposed to this one which is relatively simple, but horrible. =D I was mostly just curious.
+5  A: 

As an alternative approach, have you ever considered offering up a different class based on the type of the object that is asking for the class. Say the following

public interface IC {
  int DoSomething();
}

public static CFactory { 
  public IC GetC(Type requestingType) { 
    if ( requestingType == typeof(BadType1) ) { 
      return new CForBadType1();
    } else if ( requestingType == typeof(OtherType) { 
      return new CForOtherType();
    }  
    ...
  }
}

This would be a much cleaner approach than have each method change it's behavior based on the calling object. It would cleanly separate out the concerns to the different implementations of IC. Additionally, they could all proxy back to the real C implementation.

EDIT Examining the callstack

As several other people pointed out you can examine the callstack to determine what object is immediately calling the function. However this is not a foolproof way to determine if one of the objects you want to special case is calling you. For instance I could do the following to call you from SomeBadObject but make it very difficult for you to determine that I did so.

public class SomeBadObject {
  public void CallCIndirectly(C obj) { 
    var ret = Helper.CallDoSomething(c);
  }
}

public static class Helper {
  public int CallDoSomething(C obj) {
    return obj.DoSomething();
  }
}

You could of course walk further back on the call stack. But that's even more fragile because it may be a completely legal path for SomeBadObject to be on the stack when a different object calls DoSomething.

JaredPar
Would this still require A and B to provide their type to the CFactory?How would I prevent spoofing?
@devinb, you can walk the stack to prevent spoofing but it's not a foolproof way. .Net just wasn't designed for a function to know it's caller and as a result any attempt to get it to work will have holes.
JaredPar
I'm trying to code with the paradigm that a malicious coder will have access to my source, but not be able to change it. But I think you're right in that it's nearly impossible to exclude all "badObject" possibilities
A: 

Well you could try grabbing the stack trace and determine the type of the caller from there, which is an overkill in my opinion and would be slow.

How about an interface, that A,B would implement?

interface IFoo { 
     int Value { get; } 
}

And then your DoSomething method would look like this:

   public int DoSomething(int input, IFoo foo)
   {
        return input + foo.Value;
   }
arul
A: 

You could use the System.Diagnostics.StackTrace class to create a stack trace. Then you could look for the StackFrame that is associated with the caller. The StackFrame has a Method property that you can use to get to the type of the caller.

However, the above method is nothing that should be used in performance critical code.

EFrank
A: 

You could examine the call stack, but that is both expensive and fragile. When your code is jit'ed the compiler might inline your methods so while it could work in debug mode you could get a different stack when compiled in release mode.

JoshBerke
+1  A: 

The easiest answer would be to pass in the sender object like any event with the typical sender, eventargs methodology.

Your calling code would look like this:

return c.DoSomething(input, this);

Your DoSomething method would simply check the type using the IS operator:

public static int DoSomething(int input, object source)
{
    if(source is A)
        return input + 1;
    else if(source is B)
        return input + 2;
    else
        throw new ApplicationException();

}

This seems like something with a little more OOP. You might consider C an abstract class with an method, and having A,B inherit from C and simply call the method. This would allow you to check the type of the base object, which is not obviously spoofed.

Out of curiosity, what are you trying with this construct?

Matt Murrell
Roughly speaking, C contains information for A, and information for B, and the method to access that information is identical, but B can't access (or be ABLE to access) A information, and B can't access A information.... roughly.
borisCallens
Hmm... maybe. A database contains a list of American Spies, and Russian Spies. The method returns a list. When the Americans ask, they get the American list, when the Russians ask, they get the Russian list.
And I want to make sure that they can't get each others lists. =P
A: 

structure it like a event handler, I'm sure FX cop would even suggest you do this

static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
        {
            throw new NotImplementedException();
        }
BPAndrew
... I don't understand.
I'm saying use what Matt Murrell suggested, but at least use the same convention as events are laid out. i.e pass your calling object, then your even args
BPAndrew
+2  A: 

Not reliably due to the possibility of inlining by the runtime.

hjb417
Assume these functions are too huge to inline.