February 2006 - Posts

Finding work

So yesterday was my first day as a contractor. Well, actually, it was my first day as an unemployed individual. The contract I was supposed to start on has gotten stalled in some "red-tape" and its uncertain whether this will be solved to everyone's satisfaction. Scary, to be sure. But this is one of the possibilities I planned for when deciding to go independent. Many W2 employees have a false sense of "security" when it comes to their jobs. You should always plan for the possibility of being out of work tomorrow!

I'm definitely not out in the cold. I've got a few contacts and was even billable for a few hours yesterday on some small projects. But as I look for more work and do more research I'm wondering if anyone has had success with some of the Social Networking Services (SNS) out there. In particular, I've heard some good things about both LinkedIn and ZeroDegrees. Both are free services and work on the concept that everyone in the world may be no more than six people away from anyone else. I plan on joining both and see how they work out. Please post a comment if you've had success (or failure!) using any of these types of services.

Posted by PSteele | 3 comment(s)

The EventLogTraceListener and EventLogEntryType

If you've ever used the EventLog class, you know it's a convenient way to write entries into any of the event logs on a machine. The WriteEntry method has a number of overloads and one of them allows you to specify an EventLogEntryType member to indicate the type of event log entry to create -- Information, Warning, Error, SuccessAudit or FailureAudit.

Now suppose you're using the EventLogTraceListener class to write trace messages out to the event log. You may notice that all of your trace messages go in as EventLogEntryType.Information. Now suppose you want to change that to Warning? There is no such property on the EventLogTraceListener to set the EventLogEntryType. And you can verify that it's a lost cause by pulling out Reflector and looking at the EventLogTraceListener's Write method:

public override void Write(string message)
{
      if (this.eventLog != null)
      {
            this.eventLog.WriteEntry(message);
      }
}

That particular overload always writes an event log entry as type "Information". But we can easily remedy this with our own TraceListener class.

First, I created a very simple TraceListener that writes to the Application EventLog. It accepts a string parameter in the constructor which defines the EventLogEntryType member to use when writing entries into the event log. The string comes from the "initializeData" parameter of the <add> element in our app.config file. The class exists in an assembly called "CustomListener".

using System;
using System.Diagnostics;
using System.Reflection;

namespace CustomListener
{

	public class PSListener : TraceListener
	{
		private EventLog _eventLog;
		private EventLogEntryType _logType;

		public PSListener(string logType) : base()
		{
			_eventLog = new EventLog("Application");
			_eventLog.Source = Assembly.GetEntryAssembly().GetName().Name;
			_logType = (EventLogEntryType) Enum.Parse(typeof(EventLogEntryType), logType);
		}

		public override void Write(string message)
		{
			_eventLog.WriteEntry(message, _logType);
		}

		public override void WriteLine(string message)
		{
			Write(message + Environment.NewLine);
		}

	}
}

I created a really simple test application:

using System;
using System.Diagnostics;

namespace SampleApp
{
	class Class1
	{
		[STAThread]
		static void Main(string[] args)
		{
			Trace.WriteLine("App Starting");

			Trace.WriteLine("App Ending");
		}
	}
}

Then I added my trace listeners to the TraceListeners collection via an app.config file (note that I added a regular EventLogTraceListener as well as my custom one). This is where I use the "initializeData" parameter to define the type of EventLogEntryType I want the TraceListener to use:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<system.diagnostics>
		<trace autoflush="true" indentsize="4">
			<listeners>
				<add name="myListener" type="System.Diagnostics.EventLogTraceListener" initializeData="mySource" />
				<add name="customListener" type="CustomListener.PSListener,CustomListener" initializeData="Warning" />
			</listeners>
		</trace>
	</system.diagnostics>
</configuration>

Finally, make sure the CustomListener DLL exists in the same directory as your sample app. After running, check your Application event log. You should have four new entries. Two from the default EventLogTraceListener with a source of "mySource" (Type = Information) and two from the custom trace listener with a source of "SampleApp" and a Type of Warning.

Posted by PSteele | 3 comment(s)

Instance variable initializers - C# vs. VB.NET

I ran into an interesting issue today when changing some VB.NET code. I was surprised at the outcome so I did a quick repro case in C# and didn't have a problem.

Given this simple C# app, the results are pretty easy to predict:

using System;

namespace InheritIssue
{
	class Class1
	{
		[STAThread]
		static void Main(string[] args)
		{
			Foo f = new Foo();
		}
	}

	public class Base
	{
		private int _data;

		public Base()
		{
			Init();
		}

		protected virtual void Init()
		{
			_data = 1;
			Console.WriteLine("Data initialized to " + _data);
		}
	}

	public class Foo :  Base
	{
		private Alpha a = new Alpha();

		public Foo() : base()
		{
			Console.WriteLine("hello");
		}

		protected override void Init()
		{
			base.Init();
			Console.WriteLine("Initial Age: " + a.Age);
		}
	}

	public class Alpha
	{
		private int _age = -1;

		public int Age
		{
			get { return _age; }
			set { _age = value; }
		}
	}
}

In case you don't feel like running the code, you'll get:

Data initialized to 1
Initial Age: -1
hello

Now the same code in VB.NET:

Option Strict On
Option Explicit On

Namespace InheritIssue

    Public Class Class1

        <STAThread()> _
        Shared Sub Main()
            Dim f As Foo = New Foo
        End Sub


        Public Class Base
            Private _data As Integer

            Public Sub New()
                Init()
            End Sub

            Protected Overridable Sub Init()
                _data = 1
                Console.WriteLine("Data initialized to " & _data)
            End Sub
        End Class

        Public Class Foo
            Inherits Base

            Private a As Alpha = New Alpha

            Public Sub New()
                MyBase.New()
                Console.WriteLine("hello")
            End Sub

            Protected Overrides Sub Init()
                MyBase.Init()
                Console.WriteLine("Initial Age: " & a.Age)
            End Sub
        End Class

        Public Class Alpha
            Private _age As Integer = -1

            Public Property Age() As Integer
                Get
                    Return _age
                End Get
                Set(ByVal Value As Integer)
                    _age = Value
                End Set
            End Property
        End Class

    End Class

End Namespace

This code gets to the Base class initializer and outputs "Data initialized to 1". It then throws a NullReferenceException trying to access the Age property of the "a" object. The "a" variable has not been initialized and is still null (Nothing). I thought this was odd so I checked out the IL for Foo's constructor in both the C# and VB.NET version and saw something noticeably different.

In C#, the ctor is:

.method public hidebysig specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       28 (0x1c)
  .maxstack  2
  IL_0000:  ldarg.0
  IL_0001:  newobj     instance void InheritIssue.Alpha::.ctor()
  IL_0006:  stfld      class InheritIssue.Alpha InheritIssue.Foo::a
  IL_000b:  ldarg.0
  IL_000c:  call       instance void InheritIssue.Base::.ctor()
  IL_0011:  ldstr      "hello"
  IL_0016:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001b:  ret
} // end of method Foo::.ctor

In VB.NET, there's an ever-so-slight difference:

.method public specialname rtspecialname 
        instance void  .ctor() cil managed
{
  // Code size       28 (0x1c)
  .maxstack  8
  IL_0000:  ldarg.0
  IL_0001:  call       instance void InheritIssue.Class1/Base::.ctor()
  IL_0006:  ldarg.0
  IL_0007:  newobj     instance void InheritIssue.Class1/Alpha::.ctor()
  IL_000c:  stfld      class InheritIssue.Class1/Alpha InheritIssue.Class1/Foo::a
  IL_0011:  ldstr      "hello"
  IL_0016:  call       void [mscorlib]System.Console::WriteLine(string)
  IL_001b:  ret
} // end of method Foo::.ctor

C# initialized the "a" variable to a new instance of Alpha before calling the base ctor. VB.NET did not do this. So when the Init method is called in Foo(), "a" is still null in the VB.NET version and you get a NullReferenceException. I checked the Visual Basic Language Specification for instance constructors and it states:

When a constructor's first statement is of the form MyBase.New(...), the constructor implicitly performs the initializations specified by the variable initializers of the instance variables declared in the type. This corresponds to a sequence of assignments that are executed immediately after invoking the direct base type constructor. Such ordering ensures that all base instance variables are initialized by their variable initializers before any statements that have access to the instance are executed.

Emphasis added by me. Now checking the C# spec:

When an instance constructor has no constructor initializer, or it has a constructor initializer of the form base(...), that constructor implicitly performs the initializations specified by the variable-initializers of the instance fields declared in its class. This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor and before the implicit invocation of the direct base class constructor. The variable initializers are executed in the textual order in which they appear in the class declaration.

Whoa! Important difference there (very different!). I wonder why the language designers chose such a different path on deciding when to intialize instance variables?

Posted by PSteele | 4 comment(s)

Taking the plunge!

Tomorrow begins my final week as a W2 employee. As of Monday, February 27th I become an independent contractor!

Through our local user group, I've built up a relationship with a local consulting company, SRT Solutions. They recently contacted me to see if I knew anyone for a position they were trying to fill in the automotive sector. I didn't really know anyone looking. Then they asked, "Would you be interested?". I thought it over for a few days and did some additional research, asked lots of questions and finally decided to do it!

It's an exciting opportunity to utilize .NET 2.0 in the automotive sector. Plus, I get to work with David White, a former Microsoft DE for the automotive sector who is now running his own startup company! It was an opportunity too good to pass up.

Although I've been doing a lot of research on becoming an independent contractor, I'll welcome any advice or tips to those of you who have taken the plunge yourself.

Posted by PSteele | 3 comment(s)
More Posts