Code Listings
Chapter 13: Code Contracts and Diagnostics
Conditional compilation
#define TESTMODE // #define directives must be at top of file
// Symbol names are uppercase by convention.
using System;
class Program
{
static void Main()
{
#if TESTMODE
Console.WriteLine ("in test mode!"); // OUTPUT: in test mode!
#endif
}
}
Conditional attribute
[Conditional ("LOGGINGMODE")]
static void LogStatus (string msg)
{
...
}
Alternatives to conditional attribute
using System;
using System.Linq;
class Program
{
public static bool EnableLogging;
static void LogStatus (Func<string> message)
{
string logFilePath = ...
if (EnableLogging)
System.IO.File.AppendAllText (logFilePath, message() + "\r\n");
}
}
TraceListener
// Clear the default listener:
Trace.Listeners.Clear();
// Add a writer that appends to the trace.txt file:
Trace.Listeners.Add (new TextWriterTraceListener ("trace.txt"));
// Obtain the Console's output stream, then add that as a listener:
System.IO.TextWriter tw = Console.Out;
Trace.Listeners.Add (new TextWriterTraceListener (tw));
// Set up a Windows Event log source and then create/add listener.
// CreateEventSource requires administrative elevation, so this would
// typically be done in application setup.
if (!EventLog.SourceExists ("DemoApp"))
EventLog.CreateEventSource ("DemoApp", "Application");
Trace.Listeners.Add (new EventLogTraceListener ("DemoApp"));
Code Contracts - why use code contracts?
public static bool AddIfNotPresent<T> (IList<T> list, T item)
{
Contract.Requires (list != null); // Precondition
Contract.Requires (!list.IsReadOnly); // Precondition
Contract.Ensures (list.Contains (item)); // Postcondition
if (list.Contains(item)) return false;
list.Add (item);
return true;
}
Contract.Requires<TException>
static void SetProgress (string message, int percent) // Classic approach
{
if (message == null)
throw new ArgumentNullException ("message");
if (percent < 0 || percent > 100)
throw new ArgumentOutOfRangeException ("percent");
...
}
static void SetProgress (string message, int percent) // Modern approach
{
Contract.Requires (message != null);
Contract.Requires (percent >= 0 && percent <= 100);
...
}
Contract.EndContractBlock
static void Foo (string name)
{
if (name == null) throw new ArgumentNullException ("name");
Contract.EndContractBlock();
...
}
Postconditions - Contract.Ensures
static bool AddIfNotPresent<T> (IList<T> list, T item)
{
Contract.Requires (list != null); // Precondition
Contract.Ensures (list.Contains (item)); // Postcondition
if (list.Contains(item)) return false;
list.Add (item);
return true;
}
Postconditions and Thread Safety
public class ThreadSafeList<T>
{
List<T> _list = new List<T>();
object _locker = new object();
public bool AddIfNotPresent (T item)
{
Contract.Ensures (_list.Contains (item));
lock (_locker)
{
if (_list.Contains(item)) return false;
_list.Add (item);
return true;
}
}
public void Remove (T item)
{
lock (_locker)
_list.Remove (item);
}
}
Contract.Result<T> and Contract.ValueAtReturn<T>
Random _random = new Random();
int GetOddRandomNumber()
{
Contract.Ensures (Contract.Result<int>() % 2 == 1);
return _random.Next (100) * 2 + 1;
}
Contract.OldValue<T>
static string Middle (string s)
{
Contract.Requires (s != null && s.Length >= 2);
Contract.Ensures (Contract.Result<string> ().Length <
Contract.OldValue (s).Length);
s = s.Substring (1, s.Length - 2);
return s.Trim();
}
Object Invariants
class Test
{
int _x, _y;
[ContractInvariantMethod]
void ObjectInvariant()
{
Contract.Invariant (_x >= 0);
Contract.Invariant (_y >= _x);
}
public int X { get { return _x; } set { _x = value; } }
public void Test1() { _x = -3; }
void Test2() { _x = -3; }
}
Contracts on Interfaces and Abstract Methods
[ContractClass (typeof (ContractForITest))]
interface ITest
{
int Process (string s);
}
[ContractClassFor (typeof (ITest))]
sealed class ContractForITest : ITest
{
int ITest.Process (string s) // Must use explicit implementation.
{
Contract.Requires (s != null);
return 0; // Dummy value to satisfy compiler.
}
}
ContractFailed event
Contract.ContractFailed += (sender, args) =>
{
string failureMessage = args.FailureKind + ": " + args.Message;
// Log failureMessage with unit testing framework:
// ...
args.SetUnwind();
};
Static contract checking
static void Main()
{
string message = null;
WriteLine (message); // Static checking tool will generate warning
}
static void WriteLine (string s)
{
Contract.Requires (s != null);
Console.WriteLine (s);
}
static void WriteLine (string s, bool b)
{
Contract.Requires (s != null);
if (b)
WriteLine (s); // Warning: requires unproven
}
static void WriteLine (string s)
{
Contract.Requires (s != null);
Console.WriteLine (s);
}
Enumerating processes:
foreach (Process p in Process.GetProcesses())
{
Console.WriteLine (p.ProcessName);
Console.WriteLine (" PID: " + p.Id);
Console.WriteLine (" Started: " + p.StartTime);
Console.WriteLine (" Memory: " + p.WorkingSet64);
Console.WriteLine (" CPU time: " + p.TotalProcessorTime);
Console.WriteLine (" Threads: " + p.Threads.Count);
}
Enumerating process threads:
public void EnumerateThreads (Process p)
{
foreach (ProcessThread pt in p.Threads)
{
Console.WriteLine (pt.Id);
Console.WriteLine (" State: " + pt.ThreadState);
Console.WriteLine (" Priority: " + pt.PriorityLevel);
Console.WriteLine (" Started: " + pt.StartTime);
Console.WriteLine (" CPU time: " + pt.TotalProcessorTime);
}
}
StackTrace and StackFrame:
static void Main() { A (); }
static void A() { B (); }
static void B() { C (); }
static void C()
{
StackTrace s = new StackTrace (true);
Console.WriteLine ("Total frames: " + s.FrameCount);
Console.WriteLine ("Current method: " + s.GetFrame(0).GetMethod().Name);
Console.WriteLine ("Calling method: " + s.GetFrame(1).GetMethod().Name);
Console.WriteLine ("Entry method: " + s.GetFrame
(s.FrameCount-1).GetMethod().Name);
Console.WriteLine ("Call Stack:");
foreach (StackFrame f in s.GetFrames())
Console.WriteLine (
" File: " + f.GetFileName() +
" Line: " + f.GetFileLineNumber() +
" Col: " + f.GetFileColumnNumber() +
" Offset: " + f.GetILOffset() +
" Method: " + f.GetMethod().Name);
}
Writing to the Windows Event Log:
const string SourceName = "MyCompany.WidgetServer";
if (!EventLog.SourceExists (SourceName))
EventLog.CreateEventSource (SourceName, "Application");
EventLog.WriteEntry (SourceName,
"Service started; using configuration file=...",
EventLogEntryType.Information);
Reading from the Windows Event Log:
EventLog log = new EventLog ("Application");
Console.WriteLine ("Total entries: " + log.Entries.Count);
EventLogEntry last = log.Entries [log.Entries.Count - 1];
Console.WriteLine ("Index: " + last.Index);
Console.WriteLine ("Source: " + last.Source);
Console.WriteLine ("Type: " + last.EntryType);
Console.WriteLine ("Time: " + last.TimeWritten);
Console.WriteLine ("Message: " + last.Message);
Monitoring the Windows Event Log:
static void Main()
{
EventLog log = new EventLog ("Application");
log.EnableRaisingEvents = true;
log.EntryWritten += DisplayEntry;
Console.ReadLine();
}
static void DisplayEntry (object sender, EntryWrittenEventArgs e)
{
EventLogEntry entry = e.Entry;
Console.WriteLine (entry.Message);
}
Enumerating performance counters:
PerformanceCounterCategory[] cats =
PerformanceCounterCategory.GetCategories();
foreach (PerformanceCounterCategory cat in cats)
{
Console.WriteLine ("Category: " + cat.CategoryName);
string[] instances = cat.GetInstanceNames();
if (instances.Length == 0)
{
foreach (PerformanceCounter ctr in cat.GetCounters())
Console.WriteLine (" Counter: " + ctr.CounterName);
}
else // Dump counters with instances
{
foreach (string instance in instances)
{
Console.WriteLine (" Instance: " + instance);
if (cat.InstanceExists (instance))
foreach (PerformanceCounter ctr in cat.GetCounters (instance))
Console.WriteLine (" Counter: " + ctr.CounterName);
}
}
}
Enumerating performance counters with LINQ to XML:
var x =
new XElement ("counters",
from PerformanceCounterCategory cat in
PerformanceCounterCategory.GetCategories()
where cat.CategoryName.StartsWith (".NET")
let instances = cat.GetInstanceNames()
select new XElement ("category",
new XAttribute ("name", cat.CategoryName),
instances.Length == 0
?
from c in cat.GetCounters ()
select new XElement ("counter",
new XAttribute ("name", c.CounterName))
:
from i in instances
select new XElement ("instance", new XAttribute ("name", i),
!cat.InstanceExists (i)
?
null
:
from c in cat.GetCounters (i)
select new XElement ("counter",
new XAttribute ("name", c.CounterName))
)
)
);
x.Save ("counters.xml");
Reading performance counter data:
using (PerformanceCounter pc = new PerformanceCounter ("Processor",
"% Processor Time",
"_Total"))
Console.WriteLine (pc.NextValue());
string procName = Process.GetCurrentProcess().ProcessName;
using (PerformanceCounter pc = new PerformanceCounter ("Process",
"Private Bytes",
procName))
Console.WriteLine (pc.NextValue());
Polling performance counters:
static void Monitor (string category, string counter, string instance,
EventWaitHandle stopper)
{
if (!PerformanceCounterCategory.Exists (category))
throw new InvalidOperationException ("Category does not exist");
if (!PerformanceCounterCategory.CounterExists (counter, category))
throw new InvalidOperationException ("Counter does not exist");
if (instance == null) instance = ""; // "" == no instance (not null!)
if (instance != "" &&
!PerformanceCounterCategory.InstanceExists (instance, category))
throw new InvalidOperationException ("Instance does not exist");
float lastValue = 0f;
using (PerformanceCounter pc = new PerformanceCounter (category,
counter, instance))
while (!stopper.WaitOne (200, false))
{
float value = pc.NextValue();
if (value != lastValue) // Only write out the value
{ // if it has changed.
Console.WriteLine (value);
lastValue = value;
}
}
}
static void Main()
{
EventWaitHandle stopper = new ManualResetEvent (false);
new Thread (delegate()
{ Monitor ("Processor", "% Processor Time", "_Total", stopper); }
).Start();
new Thread (delegate()
{ Monitor ("LogicalDisk", "% Idle Time", "C:", stopper); }
).Start();
Console.WriteLine ("Monitoring - press any key to quit");
Console.ReadKey();
stopper.Set();
}
Creating counters:
string category = "Nutshell Monitoring";
// We'll create two counters in this category:
string eatenPerMin = "Macadamias eaten so far";
string tooHard = "Macadamias deemed too hard";
if (!PerformanceCounterCategory.Exists (category))
{
CounterCreationDataCollection cd = new CounterCreationDataCollection();
cd.Add (new CounterCreationData (eatenPerMin,
"Number of macadamias consumed, including shelling time",
PerformanceCounterType.NumberOfItems32));
cd.Add (new CounterCreationData (tooHard,
"Number of macadamias that will not crack, despite much effort",
PerformanceCounterType.NumberOfItems32));
PerformanceCounterCategory.Create (category, "Test Category",
PerformanceCounterCategoryType.SingleInstance, cd);
}
Updating a counter value:
string category = "Nutshell Monitoring";
string eatenPerMin = "Macadamias eaten so far";
using (PerformanceCounter pc = new PerformanceCounter (category,
eatenPerMin, ""))
{
pc.ReadOnly = false;
pc.RawValue = 1000;
pc.Increment();
pc.IncrementBy (10);
Console.WriteLine (pc.NextValue()); // 1011
}