April 2003 - Posts
Someone needs XP
(OK, so that's a little glib, but reading through Brad's description of where it hurts, the voices in my head were screaming "Agile! Agile!").
After seeing this macro for VB.Net, I thought I'd be clever and adapt it for C#. Then I saw this, so I needn't have bothered, but my version handles a slightly different format, so if you write your private fields like so:
SomeType foo; //_foo is OK as well - it will strip the leading underscore for the public name
it will create properties of the form:
public TypeName Foo
{
get { return foo; }
set { foo = value; }
}It's a bit buggy (it will try to create properties from public fields and output invalid code, and doesn't match the indentation of the private fields), but someone might find it useful:
Imports EnvDTE
Imports System.Diagnostics
Imports System.Text.RegularExpressions
Public Module ConvertToProperties
Sub Run()
DTE.UndoContext.Open("ConvertToProperties")
Try
Dim text As TextSelection = DTE.ActiveDocument.Selection
Dim line As String
Dim lines() As String = Split(text.Text, vbLf)
Dim match As Match
Dim r As Regex = New Regex("\s*(?(private|protected)*)\s*(?\S*)\s*(?\S*)", RegexOptions.IgnoreCase Or RegexOptions.IgnoreCase.ExplicitCapture)
text.Insert(vbCrLf, vsInsertFlags.vsInsertFlagsInsertAtEnd)
For Each line In lines
line = line.Trim
If Not line = "" Then
match = r.Match(line)
If match.Success Then
CreateProperty(text, line, match)
End If
End If
Next
text.SmartFormat()
Catch ex As System.Exception
MsgBox("There was an error while constructing Property code: " & ex.ToString & ".", MsgBoxStyle.Critical, "ConvertProperties")
End Try
DTE.UndoContext.Close()
End Sub
Sub CreateProperty(ByVal text As TextSelection, ByVal line As String, ByVal match As Match)
Dim fieldName As String
Dim strippedVariableName As String
Dim publicName As String
Dim typeName As String
Dim startChars As Char = ("_")
Dim endChars As Char = (";")
fieldName = match.Groups("fieldName").Value.Trim
fieldName = fieldName.TrimEnd(endChars)
strippedVariableName = fieldName.TrimStart(startChars)
typeName = match.Groups("typeName").Value.Trim
publicName = strippedVariableName.Substring(0, 1).ToUpper & strippedVariableName.Substring(1)
WriteProperty(text, fieldName, typeName, publicName)
End Sub
Sub WriteProperty(ByVal text As TextSelection, ByVal fieldName As String, ByVal typeName As String, ByVal publicName As String)
Dim propertyText As String = String.Format("public {0} {1}" _
& "{3}{{{3}" _
& " get {{ return {2}; }}{3}" _
& " set {{ {2} = value; }}{3}" _
& "}}{3}", _
typeName, _
publicName, _
fieldName, _
vbCrLf)
text.Insert(vbCrLf & propertyText, vsInsertFlags.vsInsertFlagsInsertAtEnd)
End Sub
End Module
Keith writes:
"I believe that it is not possible to ‘Do your best’ when it comes to developing an application. I think this because as developers we are constantly evolving in our skill sets. Even within the scope of a small, say 4-6 week project; we have learned things by the end of the 6 weeks that make us better than when we started. Thus, to do our best we would need to re-code the things done at the project outset and then at the end of the re-write we would once again be in the same boat."
Some random thoughts in response:
I think "your best" is the wrong metric by which to judge performance. You don't need to do your best, you need to get your code accepted by the customer. As long as refactoring your code helps you pass their tests more quickly, with less effort, and more reliably, then keep refactoring it, but when you reach a point where the changes to your code have little or no impact on your application's ability to pass its tests, stop.
Also, why wait until the end of the project to rewrite your software? Continuous refactoring (test, code, refactor, test, code, refactor...) allows you to instantly apply the things you have learned as you code, not six weeks later. It's not as if you'll get to the end of the project and suddenly become aware of all the things you have learned, so why not (with the above caveat) apply that knowledge immediately?
And if your customer is judging the quality of your software solely on the effort put into it, or your skill as a developer, well...I don't have an answer for that :-)
Cheers.
A colleague of mine ran into an issue with NUnit today. He was comparing a decimal value returned from a database query with the expected value, and couldn't figure out why his assertion kept failing, even though he was sure both values were equal. We looked a bit closer and saw that the expected value was 20, but the returned value was 20.0000. If you call Decimal.Equals() on those two values, it will return true, because it ignores trailing zeros. NUnit, however, calls ToString() on numeric types before it compares them, so the comparison obviously fails.
This brought up another question: why is there no method on Decimal to compare both the value and the precision of two decimal numbers? In Java, the BigDecimal class provides for it with the CompareTo() method, but Decimal.CompareTo() in .Net behaves pretty much the same as Equals(). Surely in some cases 20 should not be considered equal to 20.0000?
The only way I can see to compare value and precision is by calling Decimal.GetBits() with both values, and stepping through the two arrays.
Interesting, n'est-ce pas?
Greg writes:
"Standard, out of the box .NET collections store references of type System.Object. We have a structure that we add to a collection. We add approx 2500 structure types to this collection.
Well, duh, if we are adding a structure, which is a value type, to a collection, which stores a reference type, the structure will get boxed and allocated on the heap 2500+ times! Its like creating a reference type 2500+ times!
Solution: use a reference type (class) vs. a structure in this case."
No no no!! Changing your struct into a class is a cop-out. You'll still be creating 2,500 references, and losing the benefits of value types (of course, that's assuming you really want a value type in this case).
The solution to the boxing/collection issue is to create a strongly-typed collection using an underlying store of the same type as the objects you are storing. If you want a vector-type collection for storing Int32's, you would base it on an Int32[] array, for example. There are several tools that can help you with this, including Collection Gen and whatever this thing's called.
Roll on generics.
CruiseControl.Net is now on SourceForge. Documentation is still a bit lacking, and there are a few problems building it on VS.Net 2003 (oh God, I'm a VS.Net user - now I'll have to take this back...), but at least the source is out there now. I know there are a few people on this site looking at continuous integration and build tools, so I thought I'd give you a heads up. (For those who don't know what Cruise Control or continuous integration is, look here and here).
More Posts