Saturday, June 29, 2013

I find myself overriding ToString() a lot...

I've noticed that I like to override the ToString() method very often. For me this very normal to do, but I don't see my colleagues doing it often.

For me, the benefits are:
- Quicker debugging
- Quick and informative logging

Here's an example I set up. Below i'll put the code of a tiny domain I set up with an invoice and some invoice lines. After overriding the ToString() method, there is a whole bunch of moments where that comes in handy. 

Debugging

Since the ToString() override can be used at any time, it's even more powerfull than the DebuggerDisplayAttribute.

Let's put a breakpoint somewhere in the code, see how I can debug this. There's the System.Diagnostics.Debug.Writeline of course:

See that the invoice information is just there with 1 line of code. Let's look at some more debugging tools we got in VS. There's the immediate window:


And there's the watch:


And some other Quick Watch screens:


Logging

Quickly get all of the relevant information in your log - by just calling ToString()'ing your instance to the log:


You are using Common.Logging right?

Code

It's really easy, here's the code for all of this stuff:


// --------------------------------------------------------------------------------------------------------------------
// 
//   
// 
// 
//   The program.
// 
// --------------------------------------------------------------------------------------------------------------------

namespace ConsoleApplication1
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    /// 
    /// The program.
    /// 
    internal class Program
    {
        #region Methods

        /// 
        /// The generate random invoice.
        /// 
        /// 
        /// The .
        /// 
        private static Invoice GenerateRandomInvoice()
        {
            var random = new Random();

            var invoice = new Invoice(1, DateTime.Now);
            invoice.InvoiceoLines.Add(new InvoiceoLine("Computer", 899.00m));
            invoice.InvoiceoLines.Add(new InvoiceoLine("Printers", 49.99m));
            invoice.InvoiceoLines.Add(new InvoiceoLine("Paper", 12.50m));
            invoice.InvoiceoLines.Add(new InvoiceoLine("Some more computer supplies", 12.50m));
            invoice.InvoiceoLines.Add(new InvoiceoLine("Other stuff", 12.50m));
            int numLines = random.Next(10);
            for (int i = 0; i < numLines; i++)
            {
                invoice.InvoiceoLines.Add(new InvoiceoLine("Product id " + random.Next(100), random.Next(10000) / 100m));
            }

            return invoice;
        }

        /// 
        /// The main.
        /// 
        /// 
        /// The args.
        /// 
        private static void Main(string[] args)
        {
            Invoice invoice = GenerateRandomInvoice();

            Common.Logging.LogManager.GetCurrentClassLogger().Info(invoice);
            Console.ReadLine();
        }

        #endregion
    }

    /// 
    /// The invoice.
    /// 
    internal class Invoice
    {
        #region Constructors and Destructors

        /// 
        /// Initializes a new instance of the  class.
        /// 
        public Invoice()
        {
            this.InvoiceoLines = new List();
        }

        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// 
        /// The id.
        /// 
        /// 
        /// The date.
        /// 
        public Invoice(int id, DateTime date)
            : this()
        {
            this.Date = date;
            this.Id = id;
        }

        #endregion

        #region Public Properties

        /// 
        /// Gets or sets the date.
        /// 
        public DateTime Date { get; set; }

        /// 
        /// Gets or sets the id.
        /// 
        public int Id { get; set; }

        /// 
        /// Gets the invoice total.
        /// 
        public decimal InvoiceTotal
        {
            get
            {
                return this.InvoiceoLines.Aggregate(0, (current, invoiceoLine) => (int)(current + invoiceoLine.Amount));
            }
        }

        /// 
        /// Gets or sets the invoiceo lines.
        /// 
        public ICollection InvoiceoLines { get; set; }

        #endregion

        #region Public Methods and Operators

        /// 
        /// The to string.
        /// 
        /// 
        /// The .
        /// 
        public override string ToString()
        {
            var result = new StringBuilder();
            result.AppendLine(string.Format("Invoice - Id:{0} Date:{1}", this.Id, this.Date.ToShortDateString()));
            result.AppendLine("----------------------------------");
            this.InvoiceoLines.ToList().ForEach(line => result.AppendLine(line.ToString()));
            result.AppendLine("----------------------------------");
            result.AppendLine(string.Format("{0}\t\tUSD {1:0.00}", "Total:".ToLength(15), this.InvoiceTotal));
            return result.ToString();
        }

        #endregion
    }

    /// 
    /// The invoiceo line.
    /// 
    internal class InvoiceoLine
    {
        #region Constructors and Destructors

        /// 
        /// Initializes a new instance of the  class.
        /// 
        /// 
        /// The product.
        /// 
        /// 
        /// The amount.
        /// 
        public InvoiceoLine(string product, decimal amount)
        {
            this.Product = product;
            this.Amount = amount;
        }

        #endregion

        #region Public Properties

        /// 
        /// Gets or sets the amount.
        /// 
        public decimal Amount { get; set; }

        /// 
        /// Gets or sets the product.
        /// 
        public string Product { get; set; }

        #endregion

        #region Public Methods and Operators

        /// 
        /// The to string.
        /// 
        /// 
        /// The .
        /// 
        public override string ToString()
        {
            return string.Format("{0}\t\tUSD {1:0.00}", this.Product.ToLength(15), this.Amount);
        }

        #endregion
    }

    /// 
    /// The string extensions.
    /// 
    public static class StringExtensions
    {
        #region Public Methods and Operators

        /// 
        /// The to length.
        /// 
        /// 
        /// The input.
        /// 
        /// 
        /// The desired length.
        /// 
        /// 
        /// The .
        /// 
        public static string ToLength(this string input, int desiredLength)
        {
            if (string.IsNullOrEmpty(input) || input.Length < desiredLength)
            {
                string result = string.Empty + input;
                var postfix = new string(' ', desiredLength - result.Length);
                return result + postfix;
            }

            return input.Substring(0, desiredLength - 3) + "...";
        }

        #endregion
    }
}

1 comment: