-6

I have a class MyClass. I want to create MyClass instance in using statement and to operation something 1 in constructor, but if instance is created as nested in using statement with other instance of MyClass I want to do something 2. I have no idea how to check it. I thought about static class which check if current code statement was colled from using statement which other instance of MyClass, but i don't know how check it.

public class MyClass : IDisposable
{
    public MyClass()
    {
        if(condition)
        //do something 1
        else
        //do something 2
    }

    public void Dispose()
    {
        //do something
    }
}

        using (var mc = new MyClass()) //do something 1 in constructor
        {
            using (var mc1 = new MyClass()) //do something 2 in constructor
            {
                 using (var mc2 = new MyClass()) //do something 2 
                 {
                 }
            }
            using (var mc3 = new MyClass()) //do something 2 in constructor
            {
            }

Edit: I try to do some kind of scope. It shoud be some bigger scope then TransactionScope. In my scope i want to have fiew TransactionScopes. I want to use in whole scope the same connection with database without returning it to connection pool. So when i create the major scope in using statement I want to get new connection from pool, but if i created nested using block with my scope i want to use connection from major scope. Nested is posiible because in may major using block i can run methods thats contain another using block with my scope.

NightRex
  • 5
  • 2
  • 4
    Don't do things in the constructor – Joe Aug 04 '15 at 13:31
  • I don't understand the question. Also, you are redefining the same variable `mc` in the same scope. – crush Aug 04 '15 at 13:32
  • 1
    Aside from being really bad design, its not really possible since the compiler tears your code down and reconstructs it differently. A using statement is syntactic sugar for a fancy try/finally with an automatic dispose. – Ron Beyer Aug 04 '15 at 13:37
  • It is only a simple example to show wahat i need to do, but it can be more complicated, so i can't call the specifics method, because i can not know if this pice of code is nested or not – NightRex Aug 04 '15 at 13:38
  • 1
    The place where you use your class should tell it how to be constructed, your class shouldn't try to detect how its being used and perform different logic based on that. Compiler optimizations mangle code anyway so the way its written in the text editor is very often not how it gets compiled. You should pass a parameter into your class if you need to run logic based on something. This looks like the X-Y problem though, you need to tell us what problem you are really trying to solve, why you need this. – Ron Beyer Aug 04 '15 at 13:42
  • I try to do some kind of scope. It shoud be some bigger scope then TransactionScope. In my scope i want to have fiew TransactionScopes. I want to use in whole scope the same connection with data base without returning it to connection pool. So when i create the major scope in using statement I want to get new connection from pool, but if i created nested using block with my scope i want to use connection from major scope – NightRex Aug 04 '15 at 13:49

1 Answers1

3

The simple answer to your question is: you can't. using is pure syntactic sugar. A typical using statement:

using (MyDisposableClass a = GetMyDisposableClass())
{
   // ...
}

Gets translated directly into this:

MyDisposableClass a = null;
try {
    a = GetMyDisposableClass();
    // ...
}
finally {
    if (a != null) a.Dispose();
}

In general, in .NET you can't know from whence your code has been called much beyond the function level by reflecting the callstack. The StackFrame object from [System.Diagnostics][1] will tell you the IL offset for the current method, so I suppose if you are really determined you could take apart the IL for the current method and try and figure out where you are within any try/finally code, but that's sounding really flimsy and gross to me.

What on earth are you trying to do that you feel you must manage it like this?

To me it feels like what you want is a factory object of some kind:

public interface IMyClass { int Level { get; } } // whatever
public class MyClassFactory {
    private delegate void Notifier(int level);
    public class MyClass : IDisposable
    {
        public MyClass(int level, Notifier notifier)
        {
           _level = level;
           _notifier = notifier;
        }
        private int _level;
        private Notifier _notifier;   
        ~MyClass() { Dispose(false); }
        public int Level { get { return level; } }
        public Dispose() { Dispose(true); GC.SuppressFinalize(this); }
        private bool _disposed = false;
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposed) {
                if (disposing) {
                    notifier(Level);
                    _disposed = false;
                }
                else { throw new Exception("My class used outside using block."); }
            }
        }
    }

    private int _level = 0;

    public IMyClass Make()
    {
        return new MyClass(_level++,
                childLevel => {
                   if (childLevel == _level)
                       --_level;
                   else throw new Exception("Disposed out of order.");
                 });
    }        
}

What this does is build a factory that hides the constructor to MyClass and exposes a factory method to make IMyClass objects that you can use. It does a strict ordering such that objects are constructed and disposed in order, which more or less meets your nesting requirement. You can make the objects do something different based on their Level, if you so choose.

And I would still hesitate to use this code. It doesn't feel right, but at least it's harder to do something wrong as long as you only use one factory per method.

I'm guessing that what you really want to do is to have begin/end semantics in a code block that tracks nesting, but you want the compiler to manage that for you. That I've done with an object that takes two functions to call, one on begin and one on end:

public class BeginEnd<T> : IDisposable
{
    private Action<T> _end;
    private bool _disposed;
    private T _val;
    public BeginEnd(T val, Action<T> begin, Action<T> end)
    {
        _end = end;
        _val = val;
        begin(val);
    }
    public void Dispose() {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
    ~BeginEnd() { Dispose(false); }
    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed) {
            if (disposing) {
              _disposed = true;
              _end(_val);
            }
        }
    }
}

Which can then be used in a context like this:

public class Tracker {
    private int _level;
    public BeginEnd<int> Track()
    {
        return new BeginEnd<int>(_level++,
            lev1 => {
               Debug.WriteLine("Begin " + lev1);
            },
            lev2 => {
               Debug.WriteLine("End " + lev2);
               --_level;
            });
    }
}

//...
Tracker t = new Tracker();
using (var n0 = t.Track()) {
    using (var n1 = t.Track()) {
    }
    using (var n2 = t.Track()) { }
} 
// prints:
// Begin 0
// Begin 1
// End 1
// Begin 1
// End 1
// End 0

which correctly tracks the nesting of the using blocks by enforcing a begin/end rule.

Whether or not this is an appropriate use of the language construct has been debated before. I feel like your problem can be solved in another more appropriate way.

Community
  • 1
  • 1
plinth
  • 48,267
  • 11
  • 78
  • 120