Francesco's blog

 Tuesday, November 01, 2005

I am reviewing the chapter on execution flow in Visual Basic 2005, where I cover recursion - among the many things. In most programming books I've read, recusion is explained with the "classic" factorial example (which can be implemented more efficiently with a simple For loop) or as a means to visit tree structures. It looks like recursion isn't useful in the "average" business application, which of course isn't the case. As most programming techniques, it's mostly a matter of knowing when and where to exploit it.

Here's an example of recursion that you might find quite useful: a method that converts an integer into its textual representation, e.g. 1234 into "One Thousand Two Hundreds Thirty Four", taken from my forthcoming Microsoft Press book Programming Microsoft Visual Basic 2005.

Public Shared Function NumberToText(ByVal n As Integer) As String
  
Select Case n
     
Case Is < 0
        
Return "Minus " & NumberToText(-n)
     
Case 0
        
Return ""
     
Case 1 To 19
        
Dim arr() As String = {"One", "Two", "Three", "Four", "Five", "Six", _
            "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", _
            "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}
        
Return arr(n - 1) & " "
     
Case 20 To 99
        
Dim arr() As String = {"Twenty", "Thirty", "Forty", "Fifty", "Sixty", _
            "Seventy", "Eighty", "Ninety"}
        
Return arr(n \ 10 - 2) & " " & NumberToText(n Mod 10)
     
Case 100 To 199
        
Return "One Hundred " & NumberToText(n Mod 100)
     
Case 200 To 999
        
Return NumberToText(n \ 100) & "Hundreds " & NumberToText(n Mod 100)
     
Case 1000 To 1999
        
Return "One Thousand " & NumberToText(n Mod 1000)
     
Case 2000 To 999999
        
Return NumberToText(n \ 1000) & "Thousands " & NumberToText(n Mod 1000)
     
Case 1000000 To 1999999
        
Return "One Million " & NumberToText(n Mod 1000000)
     
Case 1000000 To 999999999
        
Return NumberToText(n \ 1000000) & "Millions " & NumberToText(n Mod 1000000)
     
Case 1000000000 To 1999999999
        
Return "One Billion " & NumberToText(n Mod 1000000000)
     
Case Else
        
Return NumberToText(n \ 1000000000) & "Billions " _
            & NumberToText(n
Mod 1000000000)
   End Select
End Function

Here's the version for curly braces' lovers. C# switch keyword doesn't support ranges, thus I had to change the code to use a series of elseif blocks:

public static string NumberToText( int n)
{
  
if ( n < 0 )
     
return "Minus " + NumberToText(-n);
  
else if ( n == 0 )
     
return "";
  
else if ( n <= 19 )
     
return new string[] {"One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", 
         "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", 
         "Seventeen", "Eighteen", "Nineteen"}[n-1] + " ";
  
else if ( n <= 99 )
     
return new string[] {"Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", 
         "Eighty", "Ninety"}[n / 10 - 2] + " " + NumberToText(n % 10);
  
else if ( n <= 199 )
     
return "One Hundred " + NumberToText(n % 100);
  
else if ( n <= 999 )
     
return NumberToText(n / 100) + "Hundreds " + NumberToText(n % 100);
  
else if ( n <= 1999 )
     
return "One Thousand " + NumberToText(n % 1000);
  
else if ( n <= 999999 )
     
return NumberToText(n / 1000) + "Thousands " + NumberToText(n % 1000);
  
else if ( n <= 1999999 )
     
return "One Million " + NumberToText(n % 1000000);
  
else if ( n <= 999999999)
     
return NumberToText(n / 1000000) + "Millions " + NumberToText(n % 1000000);
  
else if ( n <= 1999999999 )
     
return "One Billion " + NumberToText(n % 1000000000);
  
else 
     
return NumberToText(n / 1000000000) + "Billions " + NumberToText(n % 1000000000);
}

These methods are much simpler than any similar code I've found on the Internet, thanks to recursion. I really love OOP, generics, attributes, regular expressions, and other advanced language features, but I also like to reming that you can often write elegant, compact, and efficient code just leveraging the features that mainstream languages have offered for decades.

11/1/2005 5:32:09 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, October 31, 2005

VB.NET and C# compilers manage string constants in a rather smart way: all strings with same value are stored in a common area known as string intern pool. The following code snippet shows this compiler feature in action:

' VB.NET
Dim s1 As String = "ABCDE"
Dim s2 As String = "ABC" & "DE"
' Prove that s1 and s2 point to the same element in the intern pool
Console.WriteLine(s1 Is s2) ' => True

// C#
string s1 = "ABCDE";
string s2 = "ABC" + "DE";
// Prove that s1 and s2 point to the same element in the intern pool
Console.WriteLine(String.ReferenceEquals(s1, s2)); // => True

This optimization technique doesn't really have any impact on the amount of memory used by most client applications, but it makes a difference if used inside types that are instantiated thousand times, as it often happens in server applications. The problem is, this optimization is applied only to string constants, not to strings built at runtime:

' VB.NET ...continuing previous example...
Dim s3 As String = "ABC"
s3 &= "DE"
' s1 and s3 contain the same value but point to a different string
Console.WriteLine(s1 = s3) ' => True
Console.WriteLine(s1 Is s3) ' => False

// C# ... continuing previous example...
string s3 "ABC";
s3 += "DE";
Console.WriteLine(s1 == s3) // => True
Console.WriteLine(String.ReferenceEquals(s1, s3) // => False

Now, let's suppose you have a component in the data tier and this component contains the the connection string for the database. This connection string is read from somewhere - typically the configuration file - when it's time to open the connection, therefore the compiler can't store the string in the intern pool. If this component is instantiated N times, there will be N copies of the same string in memory, which clearly is a waste if the string is long and N is high. There are two ways to avoid this waste, depending on how the connection string can vary.

If the connection string is guaranteed to be the same for all the instances, then you can store it in a static variable (a Shared variable in VB), so that the string is shared among all the instances of the component. This is the simplest case and I assume you know how to implement it, so let's move to the more interesting situation.

If the connection string can vary - for example, if the data component can connect to two or more different databases or if the connection string can use different login information - you can't store it in a static field. In this case you can resort to a technique based on the String.Intern method. This method receives a string argument and searches the argument in the intern pool: if the search is successful, the method returns a pointer to the existing string in the pool; if the search fails, the method inserts the string in the pool and returns a pointer to the element just added. Here's how you might implement the ConnectionString property in the hypothetical data component to better leverage the intern pool:

' VB.NET
Dim m_ConnectionString As String

Property ConnectionString() As String
   Get
      Return m_ConnectionString
   End Get
   Set(ByVal Value As String)
      m_ConnectionString = String.Intern(Value)
   End Set
End Property
 

// C#
private string m_ConnectionString;

public string ConnectionString
{
  get { return m_ConnectionString; }
  set { m_ConnectionString = String.Intern(value);}
}

The first time a given value is assigned to the ConnectionString property, the search in the pool fails, the String.Intern method adds the string in the pool and returns a pointer to the new pool element. If the same connection string is eventualy assigned to a different instance of the data component, the String.Intern pool returns a pointer to the element already in the pool and doesn't create any duplicate. The total amount of memory that the application uses is reduced and so is the number of garbage collections that occur during the application's lifetime.

10/31/2005 6:14:23 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
 Sunday, October 30, 2005

I often need to paste a text fragment as a comment in my source code. Unfortunately I can't simply paste the text and then use the Edit-Comment Selectio command. because the Visual Studio editor - at least when working with VB.NET - tries to interpret the pasted text as code and it ruins its formatting, adds or deletes characters, and so forth. In addition to this problem, when I am preparing samples for my books I need to revise all CR-LFs in the text, to wrap longer lines, because Microsoft Press standards mandate that lines aren't longer than 92 characters. All in all, it's a real nuisance.

 

A few weeks ago I decided to avoid this waste of time and wrote a macro that would do the pasting and the formatting for me. It's a simple and tiny way to increase productivity, that allows me to focus on the things that really matter. If you like tidy code listings, I am sure you'll find this macro useful.

 

The first problem I had to solve is a limitation of the Clipboard.GetObjectData method. When invoked from a macro, this method always returns Nothing, thus I needed a different way to read the text in the Clipboard. I can surely do this with an API call or by calling a method in a separate DLL, but I thought that reading the Clipboard from a macro shouldn't be that difficult. My next attempt was based on the Paste method of the TextBox control:

 

' Retrieve the text in the clipboard

Dim tb As New TextBox

tb.Multiline = True

tb.WordWrap = False

tb.ScrollBars = ScrollBars.Both

tb.Paste()

Dim text As String = tb.Text

 

This approach works nearly always. Every now and then, in fact, the Paste method fails with a cryptic message: "Class already exists". I noticed that this error occurs only if the macro editor is open, therefore during the normal use it doesn't cause much trouble. However, once you get this error, the only way to get rid of it is by restarting Visual Studio. When I posted this first solution on my Italian blog, reader Andrea Ferendeles suggested a different approach, based on the Paste method of the TextSelection object:

 

' Read the text in the clipbard, through the Selection.Paste method. 
Dim sel As TextSelection = DirectCast(DTE.ActiveDocument.Selection, TextSelection)
Dim sp As EditPoint = sel.ActivePoint.CreateEditPoint()
sel.Paste()
' Select and read the text just pasted, then delete it

sel.MoveToPoint(sp, True)
Dim text As String = sel.Text
sel.Delete()

 

Once I solved this problem, writing the macro was relatively simple:

 

Imports EnvDTE
Imports System.Text.RegularExpressions

 

Imports CodeArchitectsMacros

 

   Public Sub PasteAsComment()
    
PasteAsComment("80")
   End Sub

   Public Sub PasteAsComment(ByVal lineLength As String)
     
Dim maxLength As Integer = CInt(lineLength)

      ' Read the text in the clipbard, through the Selection.Paste method. 
      Dim sel As TextSelection = DirectCast(DTE.ActiveDocument.Selection, TextSelection)
      Dim sp As EditPoint = sel.ActivePoint.CreateEditPoint()
      sel.Paste()
      ' Select and read the text just pasted, then delete it

      sel.MoveToPoint(sp, True)
      Dim text As String = sel.Text
      sel.Delete()

      ' Split in lines not longer than MaxLength
     
Dim result As String = ""
     
Dim currLineLength As Integer = 0
     
For Each m As Match In Regex.Matches(text, "\S+\s*")
        
If currLineLength + m.Length > maxLength Then
           
result &= ControlChars.CrLf
           
currLineLength = 0
        
End If
        
result &= m.Value
        
currLineLength += m.Length
        
If m.Value.IndexOf(ControlChars.CrLf) > 0 Then
           
currLineLength = 0
        
End If
      Next
    
  result &= ControlChars.CrLf

      ' Paste the text in the code editor
     
sp = sel.ActivePoint.CreateEditPoint()
     
sel.Insert(result)
     
sel.MoveToPoint(sp,
True)

      ' Comment and reformat it
      DTE.ExecuteCommand("Edit.CommentSelection")
     
sel.SmartFormat()
   End Sub

End Module

As you see, there are actually two macros. The version with zero arguments creates comments lines that are 80 characters or shorter; this is likely to be the version that you'll use more often and you may want to associate it with a keyboard shortcut. The version with one argument allows you to specify the line length and can be used only from the Command window. For example, the following command pastes the current Clipboard content as comments not longer than 60 characters:

 

Macros.MyMacros.UsefulMacros.PasteAsComment 60

 

You don't really have to type all these characters each time, because you can associate the command to an alias. using this command:

 

alias PasteCom Macros.MyMacros.UsefulMacros.PasteAsComment

 

Once you've created the alias, you can recall the macro as follows:

 

PasteCom 60

 


NOTE: In case you never wrote a macro in your programming life, this is how to proceed:

 

1) run the Tools-Macros-Macros IDE command (or just press Alt+F11) to bring up the Macro IDE

2) in the Macro IDE, select the MyMacros project, then issue the Projects-Add Module to create a new module that stores all your custom macros then paste the macro code inside this module. (As in previous code, most of my macros are gathered in the CodeArchitectsMacros module.)

3) Go back to Visual Studio and display the Macro Explorer window, by means of the Tools-Macros.Macro Explorer menu command (or just press Alt+F8); in the Macro Explorer window, expand the MyMacros node and then expand the CodeArchitectsMacros module

4) optionally, go to the Tools-Options dialog box to assign a keyboard shortcut to the PasteComment macro

 

You're now ready to test the macro. Switch to Notepad or Word or wherever the text is, copy it into the clipboard, switch back to Visual Studio, place the caret where you want to insert the comment, and run the macro. You can run the macro by double-clicking its node in the Macro Explorer window, by typing the keyboard shortcut (if you assigned one), or by typing the macro's name inside the Commands window (with or without its alias, see above).

 

10/30/2005 12:47:30 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [3]  | 
 Saturday, October 29, 2005

First of all, some background on delegate covariance and contravariance in C# 2.0. Let's suppose you have the following delegate definition:

delegate object GetControlData(TextBox ctrl);

Thanks to delegate covariance, this delegate can point to a method whose return value inherits from the delegate's return type. For example, covariance enables a GetControlData delegate to point to any method that takes a TextBox argument, regardless of its return value, because all .NET types inherit from System.Object. The only condition is that the method actually returns something, therefore the delegate can't point to a void method. For example, you can create a GetControlData delegate that points to the following method, because the String type inherits from Object:

string GetText(TextBox ctrl) 
{ return ctrl.Text; }

Delegate contravariance lets you create a delegate that points to a method whose argument is the base type of the argument that appears in the delegate's signature. In previous example, contravariance enables you to create a GetControlData delegate that points to a method that accepts a Control or Object value, because these types are both base types for the TextBox class specified in the delegate signature. For example, the GetControlData delegate can point to this method:

object GetTag(Control ctrl) 
{ return ctrl.Tag; }

It should be evident that covariance and contravariance don't impact code robustness and can't cause type mismatch errors at runtime. Obviously, covariance and contravariance can be combined, thus a GetControlData delegate can point to the following method:

string GetText(Control ctrl) 
{ return ctrl.Text; }

It's important to notice that - even if the target method accepts a generic Control instance - if you call this method through the delegate, you must pass a TextBox argument, because this is the type that appears in the delegate's signature.

You can find more details on what I've explained so far virtually anywhere on the 'Net, with many more examples. It's time to go back to the main reason for this post.

In case you wondered why I used C# for all the examples, here's the reason: Visual Basic 2005 supports neither covariance nor contravariance.

Is that absolutely correct? Well, yes and no. It's true because VB 2005 doesn't support these feature natively, but you can leverage them all the same. It isn't immediately apparent that covariance and contravariance are supported at the .NET Framework level. In other words, they are a feature of .NET 2.0 delegates, not just C# 2.0. In fact, in .NET 2.0 it is possible to use reflection to create a delegate that has both these properties. Here's how you can proceed if you code with Visual Basic 2005. Say you have the following delegate definition and the following method inside a Windows Form class:

Delegate Function GetControlData(ByVal ctrl As TextBox) As Object

Function GetText(ByVal ctrl As Control) As String
   Return ctrl.Text
End Function

VB 2005 doesn't support covariance and controvariance natively, therefore it isn't possible to create a GetControlData instance that points to the GetText method using pure VB code. However, you can get there anyway via reflection, by creating a MethodInfo object that points to the target method and then passing this MethodInfo object to the Delegate.CreateDelegate static method:

' the target method
Dim targetMethod As MethodInfo = Me.GetType().GetMethod("GetText")
' build the delegate through reflection
Dim deleg As GetControlData = DirectCast([Delegate].CreateDelegate( _
   GetType(GetControlData), Me, targetMethod), GetControlData)
' show that the delegate works correctly
Console.WriteLine(deleg(Me.TextBox1))

This code is only slightly slower than the direct creation of the delegate (that you can implement only in C# 2.0), but this is hardly a serious issue, because you typically create a delegate once and use it many times. Another minor problem is that this code can fail at runtime if the method name is mistyped, but on the other hand you would spot this bug the very first time you run the code.

NOTE: I haven't tested this code against the RTM version yet, but under the RC release I found the .NET support for covariance and contravariance isn't perfect. In fact, not all the overloads of the Delegate.CreateDelegate method support this feature. For example, the overload that takes the name of the target method (instead of the MethodInfo that points to the method) causes a runtime error (ArgumentException: Error binding to target method) if you attempt to create a delegate whose signature doesn't match perfectly the target method's signature.

10/29/2005 5:47:43 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Friday, October 28, 2005

I was so excited to announce the new version of the dotnet2themax site that I forgot to introduce myself, as is customary in any weblog. On the other hand, if you have attended dotnet2themax.com in the past, odds are that you already know me, perhaps because you've read one of my books or one of my articles on programming magazines. But I will summarize my professional life here, if only to provide links to other places where you can find more interesting material.

I live and work in Bari, Italy, but I often travel to speak at conferences such as VSLive!, WinDev (US), DevWeek (UK), TechDays (Switzerland), and a few others.

I am one of the two Italian MSDN Regional Directors and in the last two years I have served as the chairman for Windows Professional Conference, the largest Italian conference for developers, and routinely speak at DevDays and other Microsoft events in Italy since 1998. I have been giving classes for Wintellect in the US until mid-2004, when I decided not to spend abroad 5 or 6 months of each year.

In 2002 I founded Code Architects, a software company that focuses on .NET and Microsoft technologies, together with Giuseppe Dimauro (the other MSDN Regional Director for Italy). Code Architects provides training and consulting services for many Italial government agencies and large companies, including Microsoft. Code Architects markets a line of programming tools that I authored (or co-authored), including CodeBox, Form Maximizer, and the award-winning VB Maximizer. The Code Architects Team include some of the most skilled .NET experts in Italy. If you can read Italian, you might find a lot of interesting stuff in our Team Blog.

I wrote about 80 articles for Visual Studio Magazine (formerly Visual Basic Programmer's Journal), with which I collaborate since 1996, and also wrote a couple of articles for MSDN Magazine and for developer's sites such as DevX. For example, you can go here to read nearly 300 tips and short articles I wrote for DevX, mostly on VB6.

I don't write only for US magazines, though. I wrote my first article back in 1983, then I wrote dozens of columns for Computer Programming, the leading Italian magazine for developers. In 1995 I founded Visual Basic and .NET Journal (formerly Visual Basic Journal), the only Italian magazine entirely devoted to .NET Framework programming.

 


English

Italian

While I still am the editor-in-chief of my own magazine, in recent years I decided to write fewer articles to focus on my books and my own vb2themax.com Web site, which I founded in 1999 and that a few years ago was expanded into dotnet2themax.com to match the new C# and VB.NET contents. This site is now sponsored by Code Architects, together with the Italian web sites dotnet2themax.it and ugisharepoint.it.

Microsoft Press published my first book in 1999. Since then Programming Microsoft Visual Basic 6 has sold around 150,000 copies all over the world, including translations to Italian, Japanese, Chinese, Korean, and Spanish, and continues to sell more than many VB.NET books. In 2002 I wrote Programming Microsoft Visual Basic .NET, which the next year was upgraded into Programming Microsoft Visual Basic .NET 2003, a 1400-page textbook that cover virtually everything you need to know about VB.NET and the .NET Framework. It has been one of the VB.NET bestseller and as of today it nearly alwways appears in Amazon's Top Ten list for Visual Basic and .NET books. I also co-authored Applied Microsoft .NET Framework Programming with Microsoft Visual Basic .NET (with Jeffrey Richter) and the newer Practical Coding Guidelines and Best Practices for Visual Basic .NET and C# Developers (with Giuseppe Dimauro), a collection of over 700 rules and tips for writing robust and efficient .NET applications.

While Visual Basic is still my pet language, I write a lot of C# code as well. I am especially interested in programming techniques, algorithms, optimization, .NET internals, client-side (Windows Forms) and ADO.NET programming. I love to write addins, macros, code generators, and other tools that can make programming a more pleasant (and faster) experience, and I'll use this blog to share my findings with you all.

What else? I love music - a bit of everything, but especially jazz and fusion - and in my previous life I was even tempted to become a professional musician, until I realized that programming can be as much fun. I played my alto sax with many local combos and orchestras, but I probably reached by peak - on the fun side, at least - with Don Box's Band on the Runtime. If you are among the few millions who never listened to the Band, you can grab a video here or here. You can find some lyrics here and here.

Books | Misc
10/28/2005 4:25:13 PM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, October 27, 2005

If you're reading these lines you already know that we have completely renovated the .NET-2-The-Max site. We have a new layout, new material, and new blogs. We have less contents, too. Yes, we've decided to drop some sections from our site. Let me explain why.

When we launched the original vb2themax.com site in 1999 it was hard to find quality-level material on developers' sites. Most sites solicited tips and code samples from visitors and published it, in most cases without editing it for accuracy. Thus, we took the opposite route and decided to publish just our own material and only the best contributions from our readers. This approach and the fact that we have published new material each and every week for three years made our site very popular among VBers. (SQL Server Magazine put vb2themax in the top ten developers' site, together with first-class sites such as MSDN Online and DevX.)

More recently we lauched dotnet2themax.com. We dropped all VB6 contents to focus on .NET exclusively. The new site offered tons of links to external articles - that were categorized and searchanble, and enabled readers to filter both the index and individual articles' contents to see the material related only to VB.NET or C#. In spite of these new features, the site was basically similar to the original vb2themax and was conceived as a single-stop-shop from where developers could start their explorations.

Today, however, finding great contents on the Web is easier than ever. Most magazines are available online and for free, many Microsoft developers reveal all the .NET secrets in their blogs, and you can always use Google to discover programming gems hidden in a site or a blog you never heard before. There's no more need for a site like what dotnet2themax used to be. No need for topic categories and an internal search engine, for example. And above all, no need to update the web site on a regular basis, as if it were an online magazine.

In the new dotnet2themax.com site, Marco Bellinaso and I - and whoever will join us later - will be telling our discoveries in the .NET fields we are more familiar with, such as VB.NET and C# languages, programming techniques, ASP.NET, Windows Forms, Sharepoint, and optimization techniques. We will write our blog as frequently as possible, publish code samples and articles, upload our tools, and anything we think can be useful or interesting for you developers out there.

Happy reading!

Francesco

10/27/2005 10:05:02 AM (GMT Daylight Time, UTC+01:00)  #    Disclaimer  |  Comments [0]  | 
 
Get RSS/Atom Feed
RSS 2.0 | Atom 1.0
Search in the blog
Archive
<October 2005>
SunMonTueWedThuFriSat
2526272829301
2345678
9101112131415
16171819202122
23242526272829
303112345
Categories

Powered by: newtelligence dasBlog 1.8.5223.1