User:Henon

From eqqon

Revision as of 11:06, 9 November 2007 by Henon (Talk | contribs)
Jump to: navigation, search

C# Knowhow :: Piccolo Snippets

Contents

Henon's Blog

Formatting Exceptions for Error Parser

Navigating in Exception Stack Traces in Visual C#

Today I wrote a nice little debug helper function called PrintException. It prints the relevant informations about an exception and its inner exceptions to the output recursively. The clue is, that it does this in a format that is understood by the error parser of Microsoft Visual Studio. So if you get an exception you can easily navigate to the source locations by clicking on the lines in the printed stacktrace. It also prints inner exceptions recursively with increasing indent for better readability.

Note: Error parser formatting works, of course, only when the exception is raised in an assembly that has been compiled in debug mode. Also the redirection of standard out to Visual Studio's output window is only activated by default for a Windows Forms application not for a Console application.

A Simple Example of Usage


public static class PrintExceptionTest
{
    static void test()
    {
        try { test1(); }
        catch (Exception e) { throw new Exception("outer exception message", e); }
    }

    static void test1()
    {
        throw new InvalidOperationException("inner exception message");
    }

    public static void Main()
    {
        try { test(); }
        catch (Exception e) { Debug.PrintException(e); }
        Console.ReadLine();
    }
}

The output generated by the above example follows. Links to source files are recognized by Visual Studio's error parser. Works also in some other IDEs and editors like Scite:

********************************************************************************
Exception: "outer exception message"
--------------------------------------------------------------------------------
InnerException:
   ********************************************************************************
   InvalidOperationException: "inner exception message"
   --------------------------------------------------------------------------------
     c:\C#\snippets\print_exception.cs(58,1):   PrintExceptionTest.test1()
     c:\C#\snippets\print_exception.cs(48,1):   PrintExceptionTest.test()
   ********************************************************************************
  c:\C#\snippets\print_exception.cs(52,1):   PrintExceptionTest.test()
  c:\C#\snippets\print_exception.cs(65,1):   PrintExceptionTest.Main()
********************************************************************************


The Implementation of PrintException



using System;

public static class Debug
{
    public static void PrintException(Exception exception)
    {
        PrintException(exception, "");
    }

    public static void PrintException(Exception exception, string indent)
    {
        string stars = new string('*', 80);
        Console.WriteLine(indent + stars);
        Console.WriteLine(indent + "{0}: \"{1}\"", exception.GetType().Name, exception.Message);
        Console.WriteLine(indent + new string('-', 80));
        if (exception.InnerException != null)
        {
            Console.WriteLine(indent + "InnerException:");
            PrintException(exception.InnerException, indent + "   ");
        }
        foreach (string line in exception.StackTrace.Split(new string[] { " at " }, StringSplitOptions.RemoveEmptyEntries))
        {
            if (string.IsNullOrEmpty(line.Trim())) continue;
            string[] parts;
            parts = line.Trim().Split(new string[] { " in " }, StringSplitOptions.RemoveEmptyEntries);
            string class_info = parts[0];
            if (parts.Length == 2)
            {
                parts = parts[1].Trim().Split(new string[] { "line" }, StringSplitOptions.RemoveEmptyEntries);
                string src_file = parts[0];
                int line_nr = int.Parse(parts[1]);
                Console.WriteLine(indent + "  {0}({1},1):   {2}", src_file.TrimEnd(':'), line_nr, class_info);
            }
            else
                Console.WriteLine(indent + "  " + class_info);
        }
        Console.WriteLine(indent + stars);
    }
}

Blogged by --Henon 12:07, 9 November 2007 (CET)


This article has also been posted on The Code Project


Ruby-like instance @variables in C#

Although I am very impressed by C#, Ruby is still my favorite programming language because of it's clean design and unreached flexibility. One of the things I like about Ruby is its way to mark instance variables (in Ruby's terminology) or member variables / fields (in C# terminology). They are prefixed with the @-sign. This makes code very easy to read. To get the same effect most C# programmers rely on a kind of Hungarian style notation which means prefixing with m_. Today I discovered that the C# compiler allows the @-sign as prefix for identifiers. This is useful if you need to name an identifier e.g. a variable like a keyword. How often have I been forced to unwillingly name variables which contain a class-object klass or _class when programming in Ruby. So this is really a nice feature of C#, isn't it?

Even better, the @-prefix can also be used to mark member variables in C#. Since I am virulently against the Hungarian notation for variable names (e.g. lpSzFile) this is exactly what I was looking for. Now programming C# feels much more like Ruby for me. There are still many oddities in C# which I need to get used to or even better I need to overcome by using the new extension methods introduced by C# 3.0.

Example: The class Range

Here is the Range class, a built in Ruby feature which I instantly missed the first day I started programming C#. It is based on the class Range by Goran Mitrovic. I refactored it a bit and applied the @-notation. Those of you who love Ruby will love this style:

using System;
using System.Collections;


namespace Eqqon
{
    public struct Range : ICollection, IEnumerable
    {
        public Range(int startValue, int endValue)
        {
            @start = startValue;
            @end = 0;
            this.End = endValue;
        }

        public Range(ICollection col)
        {
            @start = 0;
            @end = ((col != null) ? col.Count : 0) - 1;
        }

        public int Start
        {
            get { return @start; }
            set
            {
                @start = value;
                if (@end < (@start - 1)) @end = @start - 1;
            }
        }

        public int End
        {
            get { return @end; }
            set
            {
                @end = (value >= @start) ? value : (@start - 1);
            }
        }

        public int Mid
        {
            get
            {
                return (@start + @end) / 2;
            }
        }

        public void Set(int start, int end)
        {
            @start = start;
            this.End = end;
        }

        public bool Contains(int value)
        {
            return (@start <= value) && (value <= @end);
        }

        public static bool Contains(int left, int value, int right)
        {
            return (left <= value) && (value <= right);
        }

        public int Saturate(int value)
        {
            return (value > @start) ? ((value < @end) ? value : @end) : @start;
        }

        public static int Saturate(int left, int value, int right)
        {
            return (value > left) ? ((value < right) ? value : right) : left;
        }

        public static Range operator &(Range r1, Range r2)
        {
            return new Range(Math.Max(r1.Start, r2.Start), Math.Min(r1.End, r2.End));
        }

        public static Range operator |(Range r1, Range r2)
        {
            return new Range(Math.Min(r1.Start, r2.Start), Math.Max(r1.End, r2.End));
        }

        public void Offset(int o)
        {
            @start += o;
            @end += o;
        }

        public void Resize(int s)
        {
            End += s;
        }

        public int Count
        {
            get { return @end - @start + 1; }
        }

        public bool Empty
        {
            get { return @end == (@start - 1); }
        }

        public int this[int index]
        {
            get
            {
                if (Count == 0) throw new InvalidOperationException("Cannot perform this operation on a Range with Count 0!");
                return @start + index % Count;
            }
        }

        public void CopyTo(Array array, int index)
        {
            if (array == null) throw new ArgumentNullException("Array must not be null!");
            if (index < 0) throw new ArgumentOutOfRangeException("index", index, "Index must not be < 0");
            if (array.Rank != 1) throw new ArgumentException("Array must not be multidimensional.", "array");
            if (!new Range(array).Contains(index)) throw new ArgumentOutOfRangeException("index");
            if (Count == 0) return;
            if (!new Range(array).Contains(index + Count - 1)) throw new ArgumentOutOfRangeException("index");
            foreach (int i in this)
            {
                array.SetValue(i, index + i - @start);
            }
        }

        public bool IsSynchronized
        {
            get { return false; }
        }

        public object SyncRoot
        {
            get { return this; }
        }

        public IEnumerator GetEnumerator()
        {
            return new Iterator(ref this);
        }

        private class Iterator : IEnumerator
        {
            public Iterator(ref Range r)
            {
                @range = r;
                Reset();
            }

            public object Current
            {
                get
                {
                    if (!@range.Contains(@pos + @range.Start)) throw new InvalidOperationException();
                    return @range[@pos];
                }
            }

            public bool MoveNext()
            {
                return @range.Contains((++@pos) + @range.Start);
            }

            public void Reset()
            {
                @pos = -1;
            }

            private Range @range;
            private int @pos;
        };

        private int @start;
        private int @end;
    }

}

Comments

If you like to comment my Blog entry, you are welcome to do so here.

--Henon 22:18, 30 October 2007 (CET)