« A Week Left! | Townhouse Gone! » |
WARNING: Technical :)
I'm not going to talk totally in depth about Borland's environment here, though I will contrast a few pieces with the Visual Studio running on Tom's desk. Don't take these comments as indicative overall; they are just particular sore points that popped out, and could very well be fixed in other Visual Studio versions.
I was mildly impressed that Delphi 2005 will pull in Visual Studio C# projects, a little less so that it won't go in the reverse direction (i.e. it won't update Microsoft's project file format), though I suppose I can understand.
They've sure set out to make the design screens similar. Non-visual components are put on the bottom, just like in Delphi 8 and Visual Studio. Compiling brings things up no problem.
Now for the persnickety bits: one thing that got my goat somewhat in Visual Studio was the slight faltering of synchronizing things. For example, if you rename a component and try double-clicking to get an event handler, it often doesn't seem to remember that you renamed it. Sometimes, fiddling is required, and it happened in a few other circumstances. Delphi 2005 was clearly better at the synchronization. Name them right the first time, though, and there's not much difference :)
One thing Delphi 2005 seemed to trip over, though, were a couple of code completion things. For example, DataGrid has a default property of Row,Cell (e.g. myGrid[1,2]). Delphi 2005 didn't bring up hints for this style of property. Most things came up with proper hints, but a few things like this did not.
One thing I was glad of in Delphi 2005 was the ability to inspect exception objects as soon as they came up. In the Visual Studio we were using, we got the bare message, but no deeper message. We didn't find out we were using the wrong password on the database connection until we wrote actual code in Visual Studio to spit out the contents of the exception.
Like I said, niggly bits.
As is almost always the case, the big thing that's different from prior development is when you use the .NET class library. That's the thing that takes the most time to learn; I had the basics on C# down within a couple of hours.
One thing you find out soon enough is that .NET uses the base class System.Object a lot. A lot. If you retrieve a value from a database, that's an Object. There are Objects peppered all over the place. It can trip you up a little if you aren't expecting it.
Now, we're going about things a little amateurly, but it can take a while to track down the fact that if you're getting "invalid casts" on some data rows where you're retrieving a description, that what you're likely getting is a DBNull object, and in order to check for that, you need to look to Convert.IsDBNull. Here's a snippet of how you'd do it (oh, I do wish it would show my indentation properly without having to use non-breaking space entities):
int rowID = (int)dgMain[dgMain.CurrentRowIndex,0];
OdbcCommand command = new OdbcCommand();
command.Connection = odbcMain;
command.CommandText = "SELECT audit_trail FROM bug "+
"WHERE id_num="+rowID;
Object o = command.ExecuteScalar();
if (Convert.IsDBNull(o))
tbDescription.Text = "";
else
{
String description = (String)o;
tbDescription.Text = description;
}
There's also a little bit of confusion over the cases "Db"/"DB" (for 'database') should be in. The value types are DB, the connection components are Db. No real idea why :)
One thing that took me a little aback, coming from a Delphi background, is that there's no global exception handler. If an exception happens, and it's not caught specifically by something, it ends your program. In Delphi's VCL (Visual Control Library), if someone mistypes a number and you don't catch it, it pops up with an error box but keeps the program alive. Be aware of this behaviour - if you don't, you might end up with your program exiting before your user can save their data!
There's a pretty interesting collection of classes and the like. Since .NET has an ultra-safety philosophy (nice to see as a contrast to the "ah, bounds-checking, schmounds-checking"), doing bit twiddling and reinterpreting a set of four bytes as a 32-bit integer becomes a lot wordier, a lot more explicit. You might want to use the BitConverter class to do some of this work.
One thing that may trip you up if you're used to using unions in C/C++ or variant records in Object Pascal is how very restricted these can be in .NET. You can do it, but you can't have value types (integers, bytes) in the same spot as reference types (strings, objects, arrays). It's the arrays that trip most people up.
You can be very explicit with your record layout, and .NET even has a means to convert parts of your structures from values to references and back again. Here's an example of a record in Delphi for .NET (the C# is similar) that maps a 4-byte array and a 32-bit unsigned integer to the same 'memory spot':
[StructLayout(LayoutKind.Explicit)]
TRDLVector = record
[FieldOffset(0)]
DW : DWord;
[FieldOffset(0),MarshalAs(UnmanagedType.ByValArray,
SizeConst=4)]
BT : array of Byte;
end;
One thing I was a little miffed about when trying to develop under .NET is the strange quality of Microsoft's documentation. Or rather, more importantly, their examples. They're very often extremely simplistic. The help for DataGrid.CurrentRowIndex is fairly representative.
I know that data-bound controls are 'bad' as a philosophy, but they're very nice for one-offs. I was substantially disappointed by the lack of support for "data-aware controls" in .NET. There's essentially one: the DataGrid. All else is handled with Bindings, making the approach slightly uneven.
Still, this is early days for me. I agree with many, many of the philosophies behind .NET, so I'm hoping there's something a little more interesting than just poring over documentation wondering 'what were they thinking here?' and 'why is this a plain object instead of, like, a data field or something' :)
Hey, it's new to me, though, and that counts for some interest :)