Blogging From Delphi to C#

Name:
Location: Struggletown, Planet Earth, Afghanistan

Software developer with a background in multimedia, video, Delphi and C#. Now working with Ruby on Rails and Adobe Flex (the future of Web apps IMHO)

Sunday, August 21, 2005

I needed to move on.

Borland is once again changing its focus, and there are suggestions that Delphi may not even be part of Borland for much longer (See CodeFez)
Ten years ago Delphi was fundamentally a far better environment than any of its peers. VB seemed like a toy when I had to use it occasionally. I had just given up my love of the Amiga and moved to Windows for a job. It was some comfort to be working above the horrors of Win32, MFC, GDI etc in the pure OO elegance of the VCL and clean simplicity of OO Pascal (as opposed to C). I could still thumb my nose at MS, and their technical incompetence.
It is testament to this, that Microsoft then head-hunted the main brains behind Delphi (Anders) and built a technically very similar environment (.Net). They have also bought many other industry gurus, and so times have changed. They now have the money, the marketing AND the technology.
Its now much harder to be a renegade, and if you're like me - single income household with a mortgage and baby - financial reality overrides technical ideology. I needed to move on.

Convincing Prospective Employers that your Delphi skills are relevent to C# / VB / .Net

So you know Delphi and find yourself in the job queue again. The Delphi job postings are much less than last time you were looking, and some of them are for converting to .Net. How do you go about crosstraining to .Net, and convincing prospective bosses that your skills are transferrable ?

1) One answer lies above; look for jobs involving converting Delphi projects to C# (or Java, if that interests you). They'll value your Delphi skills and pay you to learn another language.
2) Look into certification. There is a whole industry around training towards MCSD and MCAD, both at home with books, or via training courses. In my experience, books are a far more effective and much cheaper way to learn anything substantial.
3) Provide a Delphi to C# comparison with your resume. You
may use mine

4) At least start working towards MCSD, and say that you are in interviews and in your resume. That sounds like you are serious and approaching this crosstraining in a professional manner. Also, I would guess that whether you are 10% or 80% towards finishing would make little difference to many employers.
5) Start a real world project eg. get involved in an open source project, or build an ASP.Net website. I started this with www.seekdotnet.com and was impressed with their SQL Server package features and price, but can't speak for the quality of their service as I haven't launched my site yet. Again, mention this in your resume and in interviews.
A website project (more than a Windows Forms project) is particularly impressive as they can very easily try it out and see that you're capable of creating something real.
6) Read books about resume writing eg. "What Colour is Your Parachute" is the classic. Spend days on your resume. A day spent on it could equal a month worth of waiting for job ads to appear, waiting for them to get back to you etc.
7) Research companies you would like to work for. Microsoft provides a listing of "Partners" on its website, which is a pretty complete list of .Net shops in your area. Get a directory of your local "technology park" or precinct. Look for government innovation development programs, and the list of companies that may provide.
8) Don't just wait for job ads to appear. Print out your resume, dress up, and approach them. My theory here is that when job ads are written, critieria is decided upon based on their ideal employee with phrases such as "12 months .Net experience". Immediately you are behind with your measley 2 weeks of .Net experience. When are face to face with them in their office however, you are a real person and they can judge your character, enthusiasm etc and may even make a position for you that didn't previously exist. They may not have time to go through with the hassle of advertising when really they need you, or they may be just about to advertise. I did this for about 4 days and got one 4 week C# contract followed by a job offer, 1 call encouraging me to apply for a new position and one email asking me if I was still looking for work, 4 weeks later. This was after 3 months of answering job ads with little response. By the way, my new job still came from answering an ad.
9) Keep all job ads that interest you, even ads for the wrong job but the right sort of company. They may be worth approaching later, and may contain important info such as the name of a manager to call. Having someone to ask for is an easy way to get past a difficult receptionist. Also, if you were the second best candidate this time, you might get the job next time. Resist the urge to resent that they didn't choose you.
10) Companies using Delphi, past and present are still your friend. They will be easy to convince that your Delphi skills are valuable, even if they no longer use it. My 4 week C# contract was with a previosly Delphi based company, and my current boss has done some serious Turbo Pascal work in the past.

Good luck !

Saturday, August 20, 2005

C# Limitation 1: no Virtual Static Methods

After some months of intensive C# learning and development, I've run into few limitations - things that Anders and others must have intentionally chosen not to do. For example, "Virtual Static Methods" - sounds all academic right ? How about this :
I am writing an object messaging library, and I want to have the objects themselves recognise their own binary stream, so that when deserializing an object from binary, I can run through a list of registered message classes, call a virtual function, and if it returns true, that is the correct class to use, and so I would call its Constructor to create an instance, and then read in its properties.
C# has two barriers to this that Delphi handles fine.
1) In C#, static functions cannot be virtual. In Delphi you would declare the message base class with a static virtual method
Recognise(TStream aStream): boolean
then override it with each specific type of message. In C# a method can be static or virtual, but not both. To use a virtual instance (non-static) method I must have already created the object, but I want to use the Recognise method to determine whether to instantiate that class in the first place ! You could I suppose implement the "prototype" design pattern, where you keep a list of objects (rather than classes) available for cloning.
2) In C#, constructors cannot be virtual either, so when I know what class to create, I can't just create it polymorphically.

Thankfully C# does have a very powerful equivalent to Delphi's RTTI (Run Time Type Information) known as Reflection and we can solve these problems with it.I solved the first issue with a static property called MessageID that must be declared in every message class (non-virtually). The message creating code keeps a list of registered classes (more on this later), reads a certain byte from the incoming stream and looks for a match with the values of MessageID in the list. It now has the class required and must create it (issue 2).
To do this, it again uses reflection in a method (see CreateFromClass() below) that will create an instance of any class having a constructor with no parameters.
AliasClassFactory contains the code for registering and creating the classes. Message classes can be specifically registered with their ID as a string (the string can of course be a number).
To get the MessageID property value from any class use this utility method :

public static object GetStaticProperty(Type aType, string aProperty) {
return aType.InvokeMember(aProperty, BindingFlags.GetProperty | BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy, null, null, null);
}

Whenever a method has a parameter of type Type like aType above (did you get that?), you can call it like this with a class :
int id = (int) GetStaticProperty(typeof(MyMessage), "MessageID")
or like this with an instance of a class
int id = (int) GetStaticProperty(myMessage.GetType(), "MessageID")


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

namespace myspace {

/// Register classes with aliases, then create them from the alias.
public class AliasClassFactory {
ArrayList list;
Type baseClass;

/// helper class used for each item in the list of classes
protected internal class AliasClassItem {
Type vClass;
string vAlias;

public Type Class {
get {
return vClass;
}
}
public string Alias {
get {
return vAlias;
}
}

public AliasClassItem(Type aClass, string aAlias) : base() {
this.vClass = aClass;
this.vAlias = aAlias;
}
}

///Create factory. Registered classes must descend from base class given here
///(can be object)
public AliasClassFactory(Type aBaseClass) : base () {
baseClass = aBaseClass;
list = new ArrayList();
}

///Classes must be registered with an alias before being created by the factory
public void Register(Type aClass, string aAlias) {
if (!aClass.IsSubclassOf(baseClass))
throw new Exception("Class must be a subclass of "+baseClass.FullName);

Type vClass = ClassFromAlias(aAlias);
if (vClass != null)
throw new Exception("Alias already registed with class "+vClass.FullName);

list.Add(new AliasClassItem(aClass,aAlias));
Trace.WriteLine("Class "+aClass.FullName+" was registered with alias :"+aAlias);
}

///Method for creating an object from a class. Requires constructor with no arguments.
///This limitation could be removed later.
public static object CreateFromClass(Type aClass) {
ConstructorInfo constructorInfo = aClass.GetConstructor(
BindingFlags.Instance | BindingFlags.Public,
null,
CallingConventions.HasThis,
new Type[0] {},//aPars,
null
);
if(constructorInfo != null)
return constructorInfo.Invoke(null);
else
throw new Exception("Constructor not found");
}

///Returns the class given an alias
public Type ClassFromAlias(string aAlias) {
foreach (object obj in list) {
if ( String.Compare( ((AliasClassItem) obj).Alias, aAlias, true)==0 ) {
return ((AliasClassItem) obj).Class;
}
}
return null;
}

///Creates a class given an alias.
public object CreateFromAlias(string aAlias) {
Type vClass = ClassFromAlias(aAlias);
if (vClass==null)
throw new Exception("Class not registered");
return CreateFromClass(vClass);
}
}
}