5

I have a class that is called regularly by several objects. I would like to include information in any exceptions as who the creater of the object is. What are my options?

"Who" refers to an object or class

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Brad
  • 20,302
  • 36
  • 84
  • 102
  • 3
    can you elaborate to what you mean by "who" ? Do you want to know under which user it was created? Or what class/function? etc.. – 7wp Aug 30 '09 at 13:59

10 Answers10

5

Store a stacktrace when the constructor is called. This is similar to what SlimDX does in debug builds.

Cecil Has a Name
  • 4,962
  • 1
  • 29
  • 31
  • 1
    One caveat on relying on the stack: It can and will differ in debug + release mode depending on optimisations etc. I recently had to fix a class that would put out erroneous information in release mode -- It made assumptions about the number of stack frames to traverse to get the caller. However, the JIT inlined various calls at its discretion. As a result, the debug & release mode functionality was not consistent. Just something to be aware of :) – Mark Simpson Aug 31 '09 at 00:09
  • I am aware, but that's just what you can get from a stack trace. In this case the program's behaviour does not depend on the trace, only the tech support. I'm confident that reference type constructors are never inlined, though, other methods should be decorated with `[MethodImpl(MethodImplOptions.NoInline)]` (if I remember the "spelling" correctly). – Cecil Has a Name Aug 31 '09 at 00:25
2

I might be missing something, but I am pretty sure that the only way you can do it, is by manually passing this information, fe. in constructor of your object.
Edit : If this is what you were looking for? :


class Creator
{
    public string Name { get; private set; }

    public Creator(string name)
    {
        Name = name;
    }
}

class Foo
{
    readonly Creator creator;
    public Foo(Creator creator)
    {
        this.creator = creator;
    }

    public void DoSth()
    {
        throw new Exception("Unhandled exception. My creator is " + creator.Name);
    }
}

public static void Main()
{
    Foo f = new Foo(new Creator("c1"));
    f.DoSth();
}
Marcin Deptuła
  • 11,789
  • 2
  • 33
  • 41
  • 1
    -1 This is really not a good way. First it pollutes the constructor with a confusing and out-of-the-place parameter. If I have a BankAccount class that takes a customerId parameter in its constructor, how would an extra parameter called name, creatorName, etc would look like? It would be confusing. Secondly, what if the creator class is renamed? Now you have wrong creator information unless you remembered to change the creator name throughout the codebase. You can use type information to pass the name but now you need to ensure that everyone does that. – Mehmet Aras Aug 30 '09 at 15:13
  • 1
    Polluting ctor - yes, that is an issue, renaming - no, there's no problem with that. In this simple example I've used some custom string, but normally, you would just get name of the class using reflection, so it would just work. But than again - what if you want to be able to distinguish between few creator objects that have common class, you need to give them custom name, so complete solution would be using some custom-name attribute + using reflection to get class's name. As for issue number one, solutions with dumping stack trace might be better, your right on that. – Marcin Deptuła Aug 30 '09 at 15:18
2

If it's only for debugging purposes then add a local string field to the class and assign Environment.StackTrace to it in the class constructor. Then you can include the information in the exceptions as well.

Filip Navara
  • 4,818
  • 1
  • 26
  • 37
1

One choice would be to put a "parent" reference into the object:

MyObject myObj = new MyObject(this);

and then use that.

ChrisF
  • 134,786
  • 31
  • 255
  • 325
1

You can try to gather some information from the stack trace in the constructor of your object. You can get the stack frames StackTrace.GetFrames. Then you can walk the stack and try to get the type that a method belongs. If that type is different than the type of your object, you stop walking and store that type information within your object. Then when an exception occurs, you can include that information along with the exception.

Note that this will increase the cost of instantiating your object. So you should consider that and may be put in a mechanism to enable/disable it or include that piece of code only in the debug build.

Mehmet Aras
  • 5,284
  • 1
  • 25
  • 32
  • @Cecil's recommendation is actually better. Instead of taking an eager approach at getting the creator information in the constructor, take a lazy approach and incur that cost only when you need that information. In other words, just capture and store the stack trace in the constructor and iterate the stack frames to get the creator information only when you need it such as when an exception occurs. Once you have that information, you should also keep it around in case you need it again. – Mehmet Aras Aug 30 '09 at 15:22
1

The question is slightly ambiguous; it really depends what you want. Is this debug information for debug builds, or is it something you will always keep around?

If it's just debug cruft that will be removed in the released product, I'd suggest doing something like this to reduce the pollution of your class(es):

Move the creation into a factory and put the stack inspection code there. This way, the created class doesn't need to care about the stack frame stuff; it is hidden away in the factory.

You can then inject the information via a property setter if you don't mind a little pollution or you could have the factory update a list of created instances + associated creation information for each instance. You can then query this list til your heart is content. Finally, in the release build, you can remove all of this functionality with a couple of #ifdefs.

Mark Simpson
  • 23,245
  • 2
  • 44
  • 44
  • yes you are correct this is just for finding a prerelease problem. I am just a beginner and I anot sure what is ment by "Move the creation into a factory" - please explain if you have the time – Brad Aug 30 '09 at 23:34
  • A factory is a class that is solely concerned with the creation of objects. It has some advantages over simply creating objects via the constructor; if everyone requests objects via a factory, you can easily add/remove debugging code to it and track the object creations. Instead of having debug code spread around in various base/subclasses, you will have the code in (mostly) one place. It may be suitable for your purposes. http://en.wikipedia.org/wiki/Factory_method_pattern – Mark Simpson Aug 31 '09 at 00:03
0

I'd recommend using the StackTrace object and/or it's GetFrame method. I haven't tried it but it should do what you need it to do w/o having to change every instantiation of the object (assuming you're not using a Factory).

I would imagine something similar to this would work.

using System.Diagnostics;
// get call stack
StackTrace stackTrace = new StackTrace();

// get calling method name
Console.WriteLine(stackTrace.GetFrame(1).GetMethod().Name);

Also note that this appears to be "not quite" a duplicate, but it's close to the question linked above.

Community
  • 1
  • 1
andymeadows
  • 1,296
  • 8
  • 14
0

Something like this could work to get the calling type:

public class Foo
{

    private Type ParentAtCreation = null;

    public Foo()
    {
        ParentAtCreation = (new StackTrace())
            .GetFrame(1)
            .GetMethod()
            .DeclaringType;
    }
}
Robert Venables
  • 5,943
  • 1
  • 23
  • 35
0

You can use overloaded constructor to that class and a global variable to store that object you receive in that overloaded constructor

e.g if you want an object of class in winforms when you invoke the object to be created you use a overloaded constructor that receives an object of a form

and in the constructor you use this object to store its value in global variable like

When you declare the object as in this case i use a form to open using my currently running form then

Admin_Login ad = new Admin_Login(Enrol, this);
                ad.Show();
                this.Visible = false;

and make the current form not visible to invoke it again when i want it. I cant dispose it as its the parent of the new form now

and in Admin_Login form i have an overloaded constructor

public Admin_Login(string Enrol,Form parent)
    {
        Enrollment = Enrol;
        Parent = parent;
        InitializeComponent();
    }

where Parent is my global variable of Form

B.O.L

Mobin
  • 4,870
  • 15
  • 45
  • 51
  • Hope it gives you an idea how i am dealing with this problem now you sort out yours.... – Mobin Aug 30 '09 at 16:58
0

I'm surprised no one else has said this yet, even as a caveat to their own solution, so I will: this is generally a bad idea, and if you find yourself needing to do it, that's usually a huge indicator that something is very wrong with your design. If you go down this route to solve your problem, you will end up causing far larger issues down the road. Best to take 2 steps back and fix the design that is making this necessary.

Rex M
  • 142,167
  • 33
  • 283
  • 313