[Mono-list] Got Switches?

Jonathan Pryor jonpryor@vt.edu
18 Dec 2002 15:35:28 -0500


Switch support has landed in Mono CVS!

What, I hear you ask, are Switches?  Aside from a programming construct?

Switches are a way to get flags/control information into your program so
they can be used.  As described in MSDN documentation, they're primarily
used with code instrumentation, but they could certainly be used for
more.

Consider the scenario where you want to have trace/debug messages
throughout your code using the System.Diagnostics.Trace and
System.Diagnostics.Debug classes.  Simple enough:

	// program.cs
	using System.Diagnostics;
	public class R {
		public static void Main () {
			Trace.WriteLine ("Hello, world!");
		}
	}

You compile it, run it, and get no output.  Oops.  That's by design; you
don't want to always see the trace messages.  Even better (worse?), if
you run monodis on the resulting executable, the call to Trace.WriteLine
isn't present.

Well, you just tripped over the fact that most methods on
System.Diagnostics.Trace are marked as [Conditional("TRACE")], meaning
you have to define the preprocessor symbol TRACE for them to be invoked.

That's easy to do: mcs -d:TRACE program.cs

You still won't get any output, though.  The Default Trace Listener
doesn't produce output; things would get messy too fast.  To see the
output, set MONO_TRACE: MONO_TRACE=Console.Out mono program.exe

Alternatively, you can add additional TraceListeners (such as for a
file), using:

	Trace.Listeners.Add (new TextWriterTraceListener(Console.Out));

Or, for those of you who hate adding additional code, see later in this
message.

Now, assume that you have a large program that makes use of the Trace
facility (like mcs/tools/type-reflector.exe).  Everything is writing
directly to Trace, resulting in lots of output.

We can minimize the output by using a different function on Trace,
WriteLineIf:

	using System.Diagnostics;
	public class R {
		private static bool print = false;
		public static void Main () {
			Trace.WriteLine ("Hello, world!");
			Trace.WriteLineIf (print, 
				"Goodbye, cruel world!");
		}
	}

So, we can narrow down when messages are printed.  But how should we set
the boolean condition used by WriteLineIf?

Well, it's taken forever, but here's where Switches come in.  .NET
defines two switches, the BooleanSwitch (either on or off) and the
TraceSwitch (with values from the TraceLevel enumeration, from Off (0)
to Verbose (4)).  You can create your own switches by deriving from
System.Diagnostics.Switch.

This allows us to write our program as:

	// program.cs
	using System.Diagnostics;
	public class R {
		private static TraceSwitch print = 
			new TraceSwitch ("print", "description");
		public static void Main () {
			Trace.WriteLine ("Hello, world!");
			Trace.WriteLineIf (
					print.TraceError,
					"Goodbye, cruel world!");
		}
	}

But how do we set the value of the TraceSwitch?  Through the .config
file.  Take the name of your executable, append ".config", and that's
your .config file.  It's an XML file that is used by the library, and
Switches make use of it.  You can add, remove, and clear all switches.

	<?xml version="1.0" encoding="utf-8"?>
	<!-- file: program.exe.config -->
	<configuration>
	  <system.diagnostics>
	    <switches>
	      <add name="print" value="1"/>
	    </switches>
	  </system.diagnostics>
	</configuration>

If we remove that <add/> line, then we won't see "Goodbye, cruel
world!".  If we change the value to 0 (synonymous with "off") or a
negative number, it also won't be displayed.  A value of 1 or higher
will display it.

You can add additional Switches to the file and to the code, and they'll
all get hooked up automagically.

If you want to send output to somewhere else (like a file), you can add
additional TraceListeners from the .config file as well, using the
<listeners/> element inside <trace/>:

	<?xml version="1.0" encoding="utf-8"?>
	<!-- file: program.exe.config -->
	<configuration>
	  <system.diagnostics>
	    <switches>
	      <add name="print" value="2"/>
	    </switches>
	    <trace autoflush="true" indentsize="4">
	      <listeners>
	        <add name="some name"
	          type="System.Diagnostics.TextWriterTraceListener, System"
	          initializeData="program.exe.output.txt"/>
	      </listeners>
	    </trace>
	  </system.diagnostics>
	</configuration>

However, TraceListener configuration support isn't quite where I'd like
it to be.  You've been warned.

Switches should work properly.  If it doesn't, please let me know.

For more information (beware line wrapping):
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfSystemDiagnosticsTraceSwitchClassTopic.asp
http://samples.gotdotnet.com/quickstart/aspplus/default.aspx?url=%2fquickstart%2fhowto%2fdoc%2fswitches.aspx

 - Jon