C # - need to know when a variable goes beyond

I have C ++ code that I am trying to pass to C #. The original author had a cool C ++ debugging class that works as follows:

void a() { indented_debug id; id.trace("I'm in A"); } void b() { indented_debug id; id.trace("I'm in B"); a(); } void e() { a(); } void d() { e(); } void c() { indented_debug id; id.trace("I'm in C"); a(); b(); { indented_debug id2; id2.trace("I'm still in C"); d(); } } 

And what you see in the output:

 I'm in C I'm in A I'm in B I'm in A I'm still in C I'm in A 

This makes it very easy to see not only the order in which the functions are called, but also who whom whom. The indentation (which is key here) is automatically handled by the construction and destruction of the "indent_debug" objects. Each time the "indent_debug" object is constructed, it increments the counter "how much should I back off"; every time the indent_debug object is destroyed, it decreases this counter. This is an automatic calculation of the indentation, which is the key to this class.

Of course, C # doesn't like it at all. C # does its best to make sure you are completely unable to find out when the variable is out of scope. And yes, I know how garbage collection works, and I like it, but it looks like Microsoft could provide us with the IsThisObjectUnreachable () function or something like that. Or the keyword attribute [RefCount], which means "Do reference counting on this object instead of garbage collection."

I can’t find a way to find out enough about the object to find out if it goes out of scope, is there any smart way to provide the same functionality in C #?

I should also add to this design restriction: I would prefer not to wrap all my functions when using (indent_debug id = new id) {} ​​", the idea is to have this debugging feature with minimal impact on the code and its readability .

[Added later]

This is a bit tricky, adding to the original question later, like this, but I need to write some code and can't do it in the comments.

The StackTrace method is oh-so-close to the solution I was looking for, let me explain how it looks.

 public class indented_debug { static int minFrame = 999; static void trace(string text) { StackTrace stackTrace = new StackTrace(); StackFrame[] frames = stackTrace.GetFrames(); if (frames.Length < minFrame) minFrame = frames.Length; String indent = new String(' ', (frames.Length - minFrame) * 3); Debug.WriteLine(indent + text); } } 

This is great because you don’t even have to create an object like indent_debug - the indentation is completely controlled by how deep you are on the stack. The disadvantage, of course, is that in my example, when c () calls d (), there are two additional stack frames where there is no trace, so the indent will be more than required. Rob suggested this method by adding a custom attribute to the methods that solves this problem (I did not use its code in my example, you can read it below).

But there is another problem: the concept of StackTrace does not allow adding extra indentation inside the function (for example, I have c () in my original function). I thought that the number of times the code has extra padding inside a function is quite small, so it’s probably acceptable to add a “use” block in these cases. This means the C # code looks like this:

 [IndentLog] void a() { indented_debug.trace("I'm in A"); } [IndentLog] void b() { indented_debug.trace("I'm in B"); a(); } void e() { a(); } void d() { e(); } [IndentLog] void c() { indented_debug.trace("I'm in C"); a(); b(); using (indented_debug id = new indented_debug()) { indented_debug.trace("I'm still in C"); d(); } } 

And then the id object is constructed and completed in a deterministic way, and I can create a data structure in which I associate the id with the current frame frame when it constructs, and de-associate it at completion.

+5
source share
5 answers

Based on the great contribution of this community, this is what I am going to (at least for now). Sorry, if the Hungarian notation offended you, I spent a lot of time on MFC-land and still kind of.

 internal class IndentFrame { public string m_type, m_function; public bool m_bOutput = false; public int m_nExtra = 0; public IndentFrame(StackFrame frame) { m_type = frame.GetMethod().DeclaringType.Name; m_function = frame.GetMethod().ToString(); } public bool Matches(StackFrame frame) { return (m_type == frame.GetMethod().DeclaringType.Name) && (m_function == frame.GetMethod().ToString()); } } public class IndentDebug : IDisposable { internal static List<IndentFrame> m_frames = new List<IndentFrame>(); public static void WriteLine(string text) { UpdateFrames(); // Remember that this frame produced output. m_frames[m_frames.Count - 1].m_bOutput = true; // How much indent? int nIndent = 0; foreach (IndentFrame frame in m_frames) nIndent += (frame.m_bOutput ? 1 : 0) + frame.m_nExtra; String indent = new String(' ', (nIndent - 1) * 3); Debug.WriteLine(indent + text); } internal static void UpdateFrames() { StackTrace stackTrace = new StackTrace(); StackFrame[] frames = stackTrace.GetFrames(); // frames[] are ordered such that the current frame is at [0] but we // want the topmost frame in [0] Array.Reverse(frames); // Remove any obsolete frames from our list int i; for (i = 0; i < Math.Min(m_frames.Count, frames.Length); i++) { if (frames[i].GetMethod().DeclaringType == typeof(IndentDebug)) break; if (!m_frames[i].Matches(frames[i])) break; } if (i < m_frames.Count) m_frames.RemoveRange(i, m_frames.Count - i); // Add any new frames while (m_frames.Count < frames.Length) { if (frames[m_frames.Count].GetMethod().DeclaringType == typeof(IndentDebug)) break; IndentFrame frame = new IndentFrame(frames[m_frames.Count]); m_frames.Add(frame); } } internal static void UpdateIndent(int add) { UpdateFrames(); m_frames[m_frames.Count - 1].m_nExtra += add; } public IndentDebug(string text) { IndentDebug.UpdateIndent(1); IndentDebug.WriteLine(text); } public void Dispose() { IndentDebug.UpdateIndent(-1); } } 

}

This is very close to my initial desires, in most cases you can simply add one line to the function (without requiring an additional custom property):

 void a() { IndentDebug.WriteLine("I'm in A"); } 

But if you want to get extra indentation inside the function, you can get it by adding the "using" statement:

 void c() { IndentDebug.WriteLine("I'm in C"); a(); b(); using (IndentDebug id = new IndentDebug("I'm still in C")) { d(); } } 
+1
source

The IDisposable interface is a mechanism for use in C # when you need to determine something when an object is no longer needed. So this is not a simplification of C ++, but it can certainly be done something like this:

 void a() { using(var id = new IndentedDebug()) { id.trace("I'm in A"); } } void b() { using(var id = new IndentedDebug()) { id.trace("I'm in B"); a(); } } 

And add reference counting to the IndentedDebug.Dispose method.

There may be better ways to do this using AOP or other templates, but you can do something when the variable goes beyond.

+3
source

This is the code I shot down in LinqPad that does what you want:

 void Main() { c(); } void a() { using(var id = new IndentedDebug()) { id.Trace("I'm in A"); } } void b() { using(var id = new IndentedDebug()) { id.Trace("I'm in B"); a(); } } void e() { a(); } void d() { e(); } void c() { using(var id = new IndentedDebug()) { id.Trace("I'm in C"); a(); b(); { using(var id2 = new IndentedDebug()) { id2.Trace("I'm still in C"); d(); } } } } class IndentedDebug : IDisposable { const int indentSize = 2; const char indentChar = ' '; static int indentLevel = 0; private string _indentSpaces; public IndentedDebug() { _indentSpaces = new string(indentChar, indentSize * indentLevel); ++indentLevel; } public void Trace(string message) { Console.WriteLine("{0}{1}", _indentSpaces, message); } public void Dispose() { --indentLevel; } } 

Thus, your code does not look exactly the same, but on the other hand, it does not do any “magic”: either the code itself shows you when debugging debugging ends.

+1
source

Why don't you take a look at the StackTrace class

https://msdn.microsoft.com/en-us/library/system.environment.stacktrace%28v=vs.110%29.aspx

It tells you exactly what methods were called to get to your current moment and many other things.

If you still have access to the member variable, then technically it is still in scope unless you manually released it or set it aside. Therefore, if you know a variable in the code, then it does not go beyond.

0
source

There is a very smart way to do this:

  • Global variables are available for the entire instance class.
  • Method variables are available for the entire method.
  • Any variables in brackets are inside the area inside this bracket.

Besides knowing how scope works, your id.trace () method is more like console output. You can simply replace it with Console.WriteLine ("I'm in ..."); and get the same results.

-1
source

Source: https://habr.com/ru/post/1247729/


All Articles