Using Visual Studio's Regex Find and Replace

31 Aug 2010

The Visual Studio Find and Replace dialog is often overlooked, and when parts of it are looked at (Regex searching) it often gets a bad rep. Sure it doesn’t implement all of the Regex syntax (non greedy search springs to mind), but that’s not to say it isn’t useful.

For instance, I was working on some code that involved a Model View Presenter type style, but used Subroutines (void methods) rather than WriteOnly properties for brevity (in C# you can do a Set only property in 1 line, VB it takes 5). As the View is doing nothing other than assigning labels from these “Setters” who cares how many lines it takes?

A quick breakdown of the parts of the expressions used:

Finding:
{}		//Tag an expression, used in replacements.  Numbered sequentially from 1, not 0.
(.*)	//Any character, any number of times, as many as possible.
\		//escape character, allows us to search for a literal '.' or other Regex used symbol.

Replacing:
\1		//The content of a tagged expression.
\n		//New line
\t		//Tab (although after running all these find and replaces, a quick {CTRL+E, CTRL+D} (format document) does most of the tidying for you).

So we start with the Interface:

Public Interface IProcessDetailsView
	Sub FileID(ByVal value As Integer)
	Sub SubmittedBy(ByVal value As String)
	Sub ReceivedDate(ByVal value As DateTime)
	//...
End Interface

So in the find and replace dialog I enter the following:

Find what:
Sub {(.*)}\(ByVal value As {(.*)}\)

Replace with:
WriteOnly Property \1() As \2

The interface definition now changes to this:

Public Interface IProcessDetailsView
	WriteOnly Property FileID() As Integer
	WriteOnly Property SubmittedBy() As String
	WriteOnly Property ReceivedDate() As DateTime
	//...
End Interface

Not too difficult right? Good. Now onto the View’s methods:

Public Class ProcessDetails
	Implements IProcessDetailsView

	Public Sub FileID(ByVal value As Integer) Implements IProcessDetailsView.FileID
		lblFileID.Text = value.ToString
	End Sub

	Public Sub SubmittedBy(ByVal value As String) Implements IProcessDetailsView.SubmittedBy
		lblAccountName.Text = value
	End Sub

	//...
End Class

Into the find and replace dialog:

Find what:
Public Sub {(.*)}\(ByVal value As {(.*)}\) Implements IProcessDetailsView\.(.*)

Replace with:
Public WriteOnly Property \1() As \2 Implements IProcessDetailsView.\1\n\t\tSet(ByVal value As \2)

Find what:
End Sub

Replace with:
End Set\n\tEnd Property

You could do this with one expression, although I have found its far less hassle to use two find and replace runs rather than trying to find new lines etc

Public Class ProcessDetails
	Implements IProcessDetailsView

	Public WriteOnly Property FileID() As Integer Implements IProcessDetailsView.FileID
		Set(ByVal value As Integer)
			lblFileID.Text = value.ToString
		End Set
	End Property

	Public WriteOnly Property SubmittedBy() As String Implements IProcessDetailsView.SubmittedBy
		Set(ByVal value As String)
			lblAccountName.Text = value
		End Set
	End Property

End Class

Now the main reason for this change was the presenter code, which doesn’t sit right with me. At a glance, am I expecting something to be calculated or what?

Public Sub Display(ByVal processHistory As ICVProcessHistory)
	_view.FileID(processHistory.FileID)
	_view.SubmittedBy(processHistory.AccountName)
	//...
End Sub

Find and replace dialog again:

Find what:
\_view\.{(.*)}\({(.*)}\.{(.*)}\)

Replace with:
_view.\1 = \2.\3

Which gives us this:

Public Sub Display(ByVal processHistory As ICVProcessHistory)
	_view.FileID = processHistory.FileID
	_view.SubmittedBy = processHistory.AccountName
	//...
End Sub

Much better in my opinion.

c#

« Multilining If statements conditions should be banned. now. To mutate or not to mutate »