October 2009 - Posts
If you are building a WinForms application and want to style your text, you may want to use the RichTextBox control instead of the standard TextBox control. This post provides some of the common techniques required to style text in a RichTextBox control.
To try out the examples in this post, build a new WinForms application. If desired, change the name of the form to "RichTextBoxSampleWin". Add a RichTextBox and two buttons to the form.
Setting Fonts/Colors
To set the fonts and colors of a RichTextBox, always select the text to style first, then set its font and color.
To try out setting fonts and colors, add this code to the Load event for the form:
In C#:
richTextBox1.Text = "That that is, is. That that is not, is not. "
+ "Is that it? It is.";
// Set the style of the text
richTextBox1.SelectionLength = richTextBox1.Text.Length;
richTextBox1.SelectionFont = new Font("Arial", 12, FontStyle.Bold);
richTextBox1.SelectionColor = Color.DarkSlateBlue;
richTextBox1.SelectionStart = richTextBox1.Text.Length;
In VB:
richTextBox1.Text = "That that is, is. That that is not, is not. " & _
"Is that it? It is."
' Set the style of the text
richTextBox1.SelectionLength = richTextBox1.Text.Length
richTextBox1.SelectionFont = New Font("Arial", 12, FontStyle.Bold)
richTextBox1.SelectionColor = Color.DarkSlateBlue
richTextBox1.SelectionStart = richTextBox1.Text.Length
The first line of code sets text into the RichTextBox.
The most important step here is to select the text that you want to style. Use the SelectionStart property to set the location in the text string to start the selection. If you don't specify a selection start, it is assumed to start at the first character of the text. Use the SelectionLength property to specify the length of the selection. In this case, the entire length of text is selected to be styled.
NOTE: Instead of setting the SelectionStart and SelectionLength properties, you can use the Select method to set both the selection start and selection length at one time.
The next two lines set the font and color for the selected text. The last line resets the SelectionStart to the end of the text. This allows the user to type in further text.
If you run the application at this point, it should look like this:
If the user types in more text, it will follow the given style.
Setting Multiple Fonts/Colors
There are two basic ways to set multiple fonts and colors within the text:
- Add the text by appending to the Text property and then use SelectionStart and SelectionLength to select the appended text. Then style the selected text. This is similar to the approach used above.
- Set the styles and then use the AppendText property.
To try out technique #1, add the following code to the button click event for one of the buttons on the form:
In C#:
// Add another set of text in a different style
string addedText = Environment.NewLine + Environment.NewLine
+ "This famous quote was from what movie?";
int len = richTextBox1.Text.Length;
richTextBox1.Text += addedText;
richTextBox1.SelectionStart = len;
richTextBox1.SelectionLength = addedText.Length;
richTextBox1.SelectionFont = new Font("Verdana", 10,
FontStyle.Regular);
richTextBox1.SelectionColor = Color.DarkSlateGray;
In VB:
Dim addedText As String = Environment.NewLine & Environment.NewLine & _
"This famous quote was from what movie?"
Dim len As Integer = richTextBox1.Text.Length
richTextBox1.Text &= addedText
richTextBox1.SelectionStart = len
richTextBox1.SelectionLength = addedText.Length
richTextBox1.SelectionFont = New Font("Verdana", 10, FontStyle.Regular)
richTextBox1.SelectionColor = Color.DarkSlateGray
This code first stores the length of the current text. This will be the selection start value. The additional text is then appended to the Text property of the RichTextBox. The SelectionStart and SelectionLength are then set as in the prior example. Finally, the font and color are set.
NOTE: You must set the SelectionStart and SelectionLength after you append the text.
To try out technique #2, replace the code in the button click event with this code instead:
In C#:
// Add another set of text in a different style
string addedText = Environment.NewLine + Environment.NewLine
+ "This famous quote was from what movie?";
richTextBox1.SelectionFont = new Font("Verdana", 10,
FontStyle.Regular);
richTextBox1.SelectionColor = Color.DarkSlateGray;
richTextBox1.AppendText(addedText);
In VB:
' Add another set of text in a different style
Dim addedText As String = Environment.NewLine & Environment.NewLine & _
"This famous quote was from what movie?"
richTextBox1.SelectionFont = New Font("Verdana", 10, FontStyle.Regular)
richTextBox1.SelectionColor = Color.DarkSlateGray
richTextBox1.AppendText(addedText)
Notice how much shorter this option is. This technique sets the SelectionFont and SelectionColor and then uses the AppendText method to add the styled text to the RichTextBox.
Regardless of which technique you choose, the result will appear as follows:
Highlighting Words
Another common requirement when working with a RichTextBox is to highlight multiple occurrences of a specific word.
To try out this technique, add this code to the other button click event:
In C#:
string wordToFind = "is";
int startIndex = 0;
while (startIndex > -1)
{
startIndex = richTextBox1.Find(wordToFind, startIndex + 1,
richTextBox1.Text.Length,
RichTextBoxFinds.WholeWord);
if (startIndex > -1 )
{
richTextBox1.Select(startIndex, wordToFind.Length);
richTextBox1.SelectionFont = new Font("Verdana", 12,
FontStyle.Bold | FontStyle.Italic);
richTextBox1.SelectionColor = Color.Red;
}
}
In VB:
Dim wordToFind As String = "is"
Dim startIndex As Integer = 0
Do While startIndex > -1
startIndex = richTextBox1.Find(wordToFind, startIndex + 1, _
richTextBox1.Text.Length, _
RichTextBoxFinds.WholeWord)
If startIndex > -1 Then
richTextBox1.Select(startIndex, wordToFind.Length)
richTextBox1.SelectionFont = New Font("Verdana", 12, _
FontStyle.Bold Or FontStyle.Italic)
richTextBox1.SelectionColor = Color.Red
End If
Loop
The code first defines the word to find. If this code is contained in a method instead of an event, the word to find could be passed in as a parameter.
The code then loops through the text in the RichTextBox using the Find method of the RichTextBox to find the next occurrence. The Find method will return –1 when it does not find any matches.
If a match is found, the code uses the Select method to select the found text. It then sets the font and color. This example also demonstrates how to bold AND italicize the text by Or'ing the style flags.
The result appears as follows:
Enjoy!
Way back when we were using Visual Studio 2005, there was a Clipboard Ring tab in the Toolbox. I loved that tab!
When I am doing a bunch of copy/pastes, I sometimes find that I hit Ctrl+C when I meant to hit Ctrl+V, and poof! my Clipboard was kindly wiped out for me.
With VS 2005, I could just open that Clipboard Ring tab of the Toolbox and there it would be ... the last several sets of Clipboard contents just waiting for me to reuse.
I was bummed when I did not see this feature in VS 2008. But behold, there are alternatives.
Accessing the Internal Clipboard Ring
Visual Studio still keeps the last sets of items you copied to the Clipboard. I heard the number was 20: that it keeps the contents from the last 20 times you used the copy function. But I did not test out that limit.
You can no longer see these last 20 clipboard items, but you can access them using keystrokes:
Ctrl+Shift+V
Keep holding down the Ctrl+Shift and pressing V to loop through the last set of clipboard items until you find the one you want.
This has saved me much frustration, knowing I can get back to my prior Clipboard contents.
Using the Toolbox
When you are using a code editor, the Toolbox displays a General tab. You can copy and paste or drag and drop code to this tab. This allows you to readily access these bits of code any time you are in a code editor.
Say I have a standard Try/Catch block that I want to use in several methods. If I plan to reuse this code often, I could create a code snippet as described here. But in this case, I just want to use this code today while I am doing some editing.
So I write the code I want, and drag it onto the General tab of the toolbox. It looks like this:
NOTE: I dragged some VB code, but this works the same in C#. It even allows me to have both VB and C# code on this tab at the same time.
If I hover over the entry, I can see the complete set of code as follows:
I can also rename the entry if I want to give it a name I can more readily recognize:
Note, however, that if the entry is renamed, hovering over the entry displays the name and not the set of code that the name represents.
I can then copy and paste from the toolbox, or just drag and drop the entry into the code editor.
If I want to manage a large set of these, I can add additional tabs to the toolbox and organize these pieces of code however I want.
If you are presenting or training, this technique is good for keeping longer code pieces so that your attendees don't need to watch you type quite so much.
Enjoy!
Visual Studio 2008 (Professional Edition and above) provides a really nice set of tools for development and execution of unit tests. It is, of course, easy for your tests to access the public properties and methods of your classes. But what about those private members?
[To begin with an overview of unit testing, start here.]
[To expose internal (C#) or friend (VB) members, see this post.]
You may have properties or methods in your class defined to be private. This post details how to test those methods using the unit testing features in Visual Studio.
NOTE: There are some testing philosophies that recommend you never test your private members; rather you test the public/internal/friend members and assume that those will provide coverage for all of your private members as well. Regardless of your point of view on this topic, this post provides the how-to so you can decide for yourself whether you want to or not.
Let's say your Customer class has a CustomerId property that looks like this:
In C#:
private int? _CustomerId;
public int? CustomerId
{
get { return _CustomerId; }
private set
{
if (_CustomerId == null || !_CustomerId.Equals(value))
{
string propertyName = "CustomerId";
if (!_CustomerId.Equals(value))
{
_CustomerId = value;
}
}
}
}
In VB:
Private _CustomerId As Integer?
Public Property CustomerId() As Integer?
Get
Return _CustomerId
End Get
Private Set(ByVal value As Integer?)
If _CustomerId Is Nothing OrElse _
Not _CustomerId.Equals(value) Then
Dim propertyName As String = "CustomerId"
If Not _CustomerId.Equals(value) Then
_CustomerId = value
End If
End If
End Set
End Property
Notice in both cases, the setter is private. The expectation is that the business object will set this value. However, you can you unit test it if you cannot set it?
The answer is PrivateObject.
PrivateObject is a class that allows test code to call class members that are not public. You PrivateObject to access private members of your class in your unit tests.
NOTE: You can also use PrivateObject for other non-public class members.
The following demonstrates how to use PrivateObject.
In C#:
/// <summary>
///A test for CustomerId
///</summary>
[TestMethod()]
public void CustomerIdTest()
{
Customer target;
int? expected;
int? actual;
// Null to Value
target = new Customer();
expected = 42;
PrivateObject po = new PrivateObject(target);
po.SetProperty("CustomerId", expected);
actual = target.CustomerId;
Assert.AreEqual(expected, actual, "Values are not equal");
}
In VB:
'''<summary>
'''A test for CustomerId
'''</summary>
<TestMethod()> _
Public Sub CustomerIdTest()
Dim target As Customer
Dim expected As Integer?
Dim actual As Integer?
' Null to value
target = New Customer
expected = 42
Dim po As PrivateObject = New PrivateObject(target)
po.SetProperty("CustomerId", expected)
actual = target.CustomerId
Assert.AreEqual(expected, actual, "Values are not equal")
End Sub
NOTE: This is only part of the code you would need to fully test the CustomerId property. Only one testing scenario was covered to instead focus on the PrivateObject syntax.
The code creates a new instance of PrivateObject, passing the instance of the class that has the private member you wish to access.
PrivateObject has methods such as GetProperty, SetProperty, GetField, SetField, and Invoke. These methods provide access to the private or other non-public members of your class.
Use PrivateObject whenever you need to access a private member in your class from your unit testing code.
Enjoy!
Visual Studio 2008 (Professional Edition and above) provides a really nice set of tools for development and execution of unit tests. It is, of course, easy for your tests to access the public properties and methods of your classes. But what about those internal/friend members?
[To begin with an overview of unit testing, start here.]
[To expose private members, see this post.]
You may have properties or methods in your class defined to be internal (C#) or friend (VB). This post details how to test those methods using the unit testing features in Visual Studio.
Well, it is easy. You just need to use this line of code:
In C#:
[assembly: InternalsVisibleTo("BoTest")]
In VB:
<Assembly: InternalsVisibleTo("BoTest")>
Where BoTest is the name of your unit testing project.
The hard part is finding where you put this line of code. It should reside in your AssemblyInfo file.
This file is relatively easy to find in your C# project; not so easy to find in your VB project. Here are the steps for both.
In C#:
- Find the project you want to test in Solution Explorer.
- Open the Properties node.
- Double-click on the AssemblyInfo.cs file.
- Set the InternalsVisibleTo attribute into the AssemblyInfo.cs file.
- Rebuild the project.
In VB:
- Find the project you want to test in Solution Explorer.
- Click on the Show All Files button in the Solution Explorer toolbar.
- Open the My Project node
- Double-click on the AssemblyInfo.vb file.
- Set the InternalsVisibleTo attribute into the AssemblyInfo.vb file.
Use this technique whenever you have internal/friend members in a class that you wish to access with your unit testing code.
Enjoy!
Visual Studio 2008 (Professional Edition and above) provides a really nice set of tools for development and execution of unit tests. This post provides suggestions for testing your property getters and setters.
[To begin with an overview of unit testing, start here.]
Let's say your Customer class has a LastName property that looks like this:
In C#:
private string _LastName;
public string LastName
{
get { return _LastName; }
set
{
if (_LastName == null || _LastName != value)
{
string propertyName = "LastName";
// Perform any validation here
if (_LastName != value)
{
_LastName = value;
SetEntityState(EntityStateType.Modified,
propertyName);
}
}
}
}
(Someone just mentioned how all of those ending braces look like a flock of sea gulls! LOL!)
In VB:
Private _LastName As String
Public Property LastName() As String
Get
Return _LastName
End Get
Set(ByVal value As String)
If _LastName Is Nothing OrElse _
_LastName IsNot value Then
Dim propertyName As String = "LastName"
' Perform any validation
If _LastName IsNot value Then
_LastName = value
SetEntityState(EntityStateType.Modified, _
propertyName)
End If
End If
End Set
End Property
NOTE: The SetEntityState method used in this code is coming from the business object base class provided in this prior post.
The key step for developing a good unit test is to define the test scenarios.
Looking at the requirements, the following testing scenarios are required for the LastName property:
- Initial null value; set to null value (should perform validation but not set the dirty flag)
- Initial null value; set to valid string (should perform validation and set the dirty flag)
- Initial null value; set to empty string (should perform validation and set the dirty flag)
- Initial string value; set to null value (should perform validation and set the dirty flag)
- Initial string value, set to different string value (should perform validation and set the dirty flag)
- Initial string value, set to same string value (should not perform validation and not set the dirty flag)
- Initial string value, set to empty value (should perform validation and set the dirty flag)
And if you have validation code, you will have more scenarios to test valid and invalid values. But this is enough to give you the general idea.
If you are building the unit test from existing code, you can generate the basic structure of the unit test for the property following the techniques detailed in this prior post.
But the test generation only gives you the basic template for testing your properties. This post focuses on how to update that template to perform the required unit testing.
Visual Studio will generate the following unit test template for the LastName property that was shown at the beginning of this post:
In C#:
/// <summary>
///A test for LastName
///</summary>
[TestMethod()]
public void LastNameTest()
{
Customer target = new Customer(); // TODO: Initialize to an appropriate value
string expected = string.Empty; // TODO: Initialize to an appropriate value
string actual;
target.LastName = expected;
actual = target.LastName;
Assert.AreEqual(expected, actual);
Assert.Inconclusive("Verify the correctness of this test method.");
}
In VB:
'''<summary>
'''A test for LastName
'''</summary>
<TestMethod()> _
Public Sub LastNameTest()
Dim target As Customer = New Customer ' TODO: Initialize to an appropriate value
Dim expected As String = String.Empty ' TODO: Initialize to an appropriate value
Dim actual As String
target.LastName = expected
actual = target.LastName
Assert.AreEqual(expected, actual)
Assert.Inconclusive("Verify the correctness of this test method.")
End Sub
To cover the defined testing scenarios, this code needs to be enhanced as follows:
In C#:
/// <summary>
///A test for LastName
///</summary>
[TestMethod()]
public void LastNameTest()
{
Customer target;
string expected;
string actual;
// Null to Null
target = new Customer();
expected = null;
target.LastName = expected;
actual = target.LastName;
Assert.AreEqual(expected, actual, "Values are not equal");
Assert.AreEqual(false, target.IsDirty,
"Object not marked as dirty");
// Null to value
target = new Customer();
expected = "Johnson";
target.LastName = expected;
actual = target.LastName;
Assert.AreEqual(expected, actual, "Values are not equal");
Assert.AreEqual(true, target.IsDirty, _
"Object not marked as dirty");
// Null to Empty
target = new Customer();
expected = string.Empty;
target.LastName = expected;
actual = target.LastName;
Assert.AreEqual(expected, actual, "Values are not equal");
Assert.AreEqual(true, target.IsDirty,
"Object not marked as dirty"); // Value to Null
target = new Customer() {LastName = "Johnson"};
target.SetEntityState(BoBase.EntityStateType.Unchanged);
expected = null;
target.LastName = expected;
actual = target.LastName;
Assert.AreEqual(expected, actual, "Values are not equal");
Assert.AreEqual(true, target.IsDirty,
"Object not marked as dirty");
// Value to new Value
target = new Customer() {LastName = "Johnson"};
target.SetEntityState(BoBase.EntityStateType.Unchanged);
expected = "Jones";
target.LastName = expected;
actual = target.LastName;
Assert.AreEqual(expected, actual, "Values are not equal");
Assert.AreEqual(true, target.IsDirty,
"Object not marked as dirty");
// Value to same Value
target = new Customer() {LastName = "Johnson"};
target.SetEntityState(BoBase.EntityStateType.Unchanged);
expected = "Johnson";
target.LastName = expected;
actual = target.LastName;
Assert.AreEqual(expected, actual, "Values are not equal");
Assert.AreEqual(false, target.IsDirty,
"Object not marked as dirty");
// Value to Empty
target = new Customer() {LastName = "Johnson"};
target.SetEntityState(BoBase.EntityStateType.Unchanged);
expected = string.Empty;
target.LastName = expected;
actual = target.LastName;
Assert.AreEqual(expected, actual, "Values are not equal");
Assert.AreEqual(true, target.IsDirty,
"Object not marked as dirty");
} In VB:
'''<summary>
'''A test for LastName
'''</summary>
<TestMethod()> _
Public Sub LastNameTest()
Dim target As Customer
Dim expected As String
Dim actual As String
' Null to Null
target = New Customer
expected = Nothing
target.LastName = expected
actual = target.LastName
Assert.AreEqual(expected, actual, "Values are not equal")
Assert.AreEqual(False, target.IsDirty, _
"Object not marked as dirty")
' Null to value
target = New Customer
expected = "Johnson"
target.LastName = expected
actual = target.LastName
Assert.AreEqual(expected, actual, "Values are not equal")
Assert.AreEqual(True, target.IsDirty, _
"Object not marked as dirty")
' Null to Empty
target = New Customer
expected = String.Empty
target.LastName = expected
actual = target.LastName
Assert.AreEqual(expected, actual, "Values are not equal")
Assert.AreEqual(True, target.IsDirty, _
"Object not marked as dirty") ' Value to Null
target = New Customer With {.LastName = "Johnson"}
target.SetEntityState(BOBase.EntityStateType.Unchanged)
expected = Nothing
target.LastName = expected
actual = target.LastName
Assert.AreEqual(expected, actual, "Values are not equal")
Assert.AreEqual(True, target.IsDirty, _
"Object not marked as dirty")
' Value to new Value
target = New Customer With {.LastName = "Johnson"}
target.SetEntityState(BOBase.EntityStateType.Unchanged)
expected = "Jones"
target.LastName = expected
actual = target.LastName
Assert.AreEqual(expected, actual, "Values are not equal")
Assert.AreEqual(True, target.IsDirty, _
"Object not marked as dirty")
' Value to same Value
target = New Customer With {.LastName = "Johnson"}
target.SetEntityState(BOBase.EntityStateType.Unchanged)
expected = "Johnson"
target.LastName = expected
actual = target.LastName
Assert.AreEqual(expected, actual, "Values are not equal")
Assert.AreEqual(False, target.IsDirty, _
"Object not marked as dirty")
' Value to Empty
target = New Customer With {.LastName = "Johnson"}
expected = String.Empty
target.LastName = expected
actual = target.LastName
Assert.AreEqual(expected, actual, "Values are not equal")
Assert.AreEqual(True, target.IsDirty, _
"Object not marked as dirty")
End Sub NOTE: The SetEntityState method and IsDirty property used in this code are coming from the business object base class provided in this prior post.
NOTE: You may get an error when calling SetEntityState because in the business object base class is defined to be protected internal (protected friend in VB), not public. If so, you need to use the technique presented here to allow your tests to access internal/friend properties and methods.
WOW! That is a LOT of test code! Some experts have said that every line of code needs at least 3 - 5 lines of test code.
We have 17 lines of C# code and 15 lines of VB code in our property procedure. If I counted correctly, there are 51 lines of C# test code and 50 lines of VB test code. So that is about 3x the number of code lines.
Let's walk through what this test code is doing, following through with the testing scenarios provided earlier in this post.
Scenario 1: The code creates a new instance of the Customer class. By default, a new instance sets any string values to null/nothing. So the test code simply sets the property to null/nothing, gets the value, confirms that the value is as expected, and ensures that it did not get marked as dirty.
Scenario 2: The code again creates a new instance of the Customer class. By default a new instance sets any string values to null/nothing. So the test code sets the property to a value, gets the value, confirms that the value is as expected, and ensures that the object was marked as dirty.
Scenario 3: The code again creates a new instance of the Customer class. By default a new instance sets any string values to null/nothing. So the test code sets the property to an empty string, gets the value, confirms that the value is as expected, and ensures that the object was marked as dirty.
Now for the harder scenarios. There is no easy way to set a non-null initial value for a property. One option is to set the private backing variable to the desired initial value. But with the backing variable being private, there is extra code to write to make it work.
Another option is to set the property using the setter. But this marks the object as dirty, adversely interfering with the test. So if you use this technique, you then need to call SetEntityState to clear the entity state and ensure it is not marked as dirty. The sample code used this technique.
Scenario 4: The code creates a new instance of the customer class, setting the initial value of the property to a valid value. It then calls SetEntityState to clear the dirty flag. Then the test code sets the property to a null, gets the value, confirms that the value is as expected, and ensures that the object was marked as dirty.
Scenario 5: The code creates a new instance of the customer class, setting the initial value of the property to a valid value. It then calls SetEntityState to clear the dirty flag. Then the test code sets the property to another valid value, gets the value, confirms that the value is as expected, and ensures that the object was marked as dirty.
Scenario 6: The code creates a new instance of the customer class, setting the initial value of the property to a valid value. It then calls SetEntityState to clear the dirty flag. Then the test code sets the property to the same value, gets the value, confirms that the value is as expected, and ensures that the object was not marked as dirty.
Scenario 7: The code creates a new instance of the customer class, setting the initial value of the property to a valid value. It then calls SetEntityState to clear the dirty flag. Then the test code sets the property to an empty string, gets the value, confirms that the value is as expected, and ensures that the object was marked as dirty.
If the field was validated, such as required field validation or a maximum length check, additional scenarios and associated test code would be required.
So for every business object property in your application, define the appropriate set of test scenarios and build the test code to support each scenario.
Or build a test base class that performs the basic set of tests for each of your properties. But that is left for a future post.
Enjoy!
Visual Studio 2008 (Team System ONLY) provides a really nice set of tools for viewing the code coverage of your unit tests.
NOTE: While Visual Studio 2008 Professional Edition has tools for building, executing, and debugging unit tests, it does NOT include the code coverage tools.
[To begin with an overview of unit testing, start here.]
Code coverage illustrates how much of the code was executed (or covered) by the unit tests.
NOTE: This post assumes you have already generated or created at least one unit test.
To view the code coverage for your unit tests:
1) Enable code coverage in the test run configuration file.
When you create your first unit test, Visual Studio adds a LocalTestRun.testrunconfig file to your solution under the Solution Items folder. Double-click on this file to open it:
Select Code Coverage from the left list:
Select the files on the right that you wish to view code coverage. Don't pick your test projects; rather pick the projects containing the code that is being tested. Then click Apply and Close.
2) Run your unit tests. The Test Results dialog appears.
See this prior post for more information on executing unit tests.
Your results should appear similar to the following:

3) If the tests pass, click the Show Code Coverage Results button in the far upper right corner of the Test Results dialog. The Code Coverage Results dialog appears.
This dialog displays all of the code in all of the files you selected for code coverage instrumentation. Drill down as needed to find the desired code.
In this case, the unit test is testing the getter and setter for the LastName. Notice that the covered percentage for both the getter and setter is 100%. That means that the unit test is executing every line of code in the getter and setter.
4) Double click on any item in the Code Coverage dialog to open the associated code window.
Notice how the code is color-coded. Blue shows code that was executed by the unit test. Red shows code that was not executed.
Use this feature to ensure you have unit tests for each unit of your code.
Enjoy!
EDITED 10/27/09: While Visual Studio 2008 Professional Edition has tools for building, executing, and debugging unit tests, it does NOT include the code coverage tools. In the original version of this post, I incorrectly stated that code coverage tools were available in the Professional Edition. This post has been corrected.
Visual Studio 2008 (Professional Edition and above) provides a really nice set of tools for development and execution of unit tests.
[To begin with an overview of unit testing, start here.]
This prior post demonstrates how to build a unit test using the "Create Unit Tests..." feature of the Code Editor. This post demonstrates how to execute a unit test.
NOTE: This post assumes you have already generated or created at least one unit test.
To execute one or more unit tests:
1) Open your solution in Visual Studio.
The solution should include both the project(s) to test and the test project(s).
2) Select Test | Windows | Test View from the menu to view the Test View window. This window contains every method marked with the TestMethod attribute.
The solution used for the screenshot above has two LastNameTest unit tests: One for the VB example and one for the C# example. If you coded along from the prior unit testing post, you will only have one for whichever language you selected. If you have been creating unit tests for one of your projects, you may have hundreds of tests in this list.
To execute unit tests:
1) Select one or more tests from the list of tests in the Test View window.
You can add columns and sort the list or use the filter feature to make it easier to locate and select the desired tests to execute.
2) Click the Run Selection button in the upper left corner of the Test View window or right-click on any selected test and select "Run Selection" from the context menu.
The selected test(s) will then execute, displaying their status in the Test Results window:
When the test is complete, the Test Results window will look something like this:
Both of the tests shown in the above screenshot are marked as Inconclusive because the generated unit testing code used the Assert class Inconclusive method. The generated unit testing template is designed to prevent a false positive. It generates an inconclusive result until you update the unit test with correct valid and invalid values and remove the Inconclusive method call.
To see more information on the result of the test, double-click on a test result.
To run the test again, use the Run or Debug buttons at the top of the Test Results window.
If any of the tests don't pass, they are marked as Failed in the Test Results window as shown below.
Double click on any of the failed tests to view the test results:
The Error Stack Trace at the bottom of this window gives you further information on the source of the failure. Click on any link in the stack trace to jump to the associated location in the code.
You can also debug the code as the test is executing if you "Debug Selection" option instead of the "Run Selection" option.
After you update the unit tests with valid code and the tests pass, the Test Results will appear as follows:
Notice the color-coded green passing indicator.
Enjoy!
If you have Visual Studio 2008 or later and have the Professional Edition or better (NOT the Express Editions), you have some very nice unit testing tools within your Visual Studio environment. These tools help you write, execute, and track your unit tests and code coverage. This post provides an introduction to using these tools.
If you are new to unit testing, the idea is to test the smallest possible units of your code. In most cases, the smallest units of code are the property procedures and methods of your classes.
For more general information about the purpose of unit testing, see this link.
NOTE: Unit testing is not meant to replace integration testing, system testing, or user testing.
In some development methodologies, such as Test Driven Development (TDD), unit tests are written before the code is written. You capture the basic requirements in the unit test and then write the code. The code is complete when the unit tests pass. Visual Studio 2010 has features to assist with this "unit test first" approach.
But this introduction demonstrates how to write unit tests for existing code. This technique is helpful if you follow a code-first approach or if you received code from another source or have old code that did not originally have unit tests.
Build the Code
The business object tested in this post is a simplified Customer class that uses the business object base class defined in this post.
The Customer class is shown below.
In C#:
public class Customer: BoBase
{
private int? _CustomerId;
public int? CustomerId
{
get { return _CustomerId; }
internal set
{
if (_CustomerId == null || !_CustomerId.Equals(value))
{
string propertyName = "CustomerId";
// Perform any validation here
if (!_CustomerId.Equals(value))
{
_CustomerId = value;
SetEntityState(EntityStateType.Modified,
propertyName);
}
}
}
}
private string _LastName;
public string LastName
{
get { return _LastName; }
set
{
if (_LastName == null || _LastName != value)
{
string propertyName = "LastName";
// Perform any validation here
if (_LastName != value)
{
_LastName = value;
SetEntityState(EntityStateType.Modified,
propertyName);
}
}
}
}
private string _FirstName;
public string FirstName
{
get { return _FirstName; }
set
{
if (_FirstName == null || _FirstName != value)
{
string propertyName = "FirstName";
// Perform any validation here
if (_FirstName != value)
{
_FirstName = value;
SetEntityState(EntityStateType.Modified,
propertyName);
}
}
}
}
private string _EmailAddress;
public string EmailAddress
{
get { return _EmailAddress; }
set
{
if (_EmailAddress == null || _EmailAddress != value)
{
string propertyName = "EmailAddress";
// Perform any validation here
if (_EmailAddress != value)
{
_EmailAddress = value;
SetEntityState(EntityStateType.Modified,
propertyName);
}
}
}
}
}
In VB:
Public Class Customer
Inherits BOBase
Private _CustomerId As Integer?
Public Property CustomerId() As Integer?
Get
Return _CustomerId
End Get
Friend Set(ByVal value As Integer?)
If _CustomerId Is Nothing OrElse _
Not _CustomerId.Equals(value) Then
Dim propertyName As String = "CustomerId"
' Perform any validation
If Not _CustomerId.Equals(value) Then
_CustomerId = value
SetEntityState(EntityStateType.Modified, propertyName)
End If
End If
End Set
End Property
Private _FirstName As String
Public Property FirstName() As String
Get
Return _FirstName
End Get
Set(ByVal value As String)
If _FirstName Is Nothing OrElse _
_FirstName IsNot value Then
Dim propertyName As String = "FirstName"
' Perform any validation
If _FirstName IsNot value Then
_FirstName = value
SetEntityState(EntityStateType.Modified, _
propertyName)
End If
End If
End Set
End Property
Private _LastName As String
Public Property LastName() As String
Get
Return _LastName
End Get
Set(ByVal value As String)
If _LastName Is Nothing OrElse _
_LastName IsNot value Then
Dim propertyName As String = "LastName"
' Perform any validation
If _LastName IsNot value Then
_LastName = value
SetEntityState(EntityStateType.Modified, _
propertyName)
End If
End If
End Set
End Property
Private _EmailAddress As String
Public Property EmailAddress() As String
Get
Return _EmailAddress
End Get
Set(ByVal value As String)
If _EmailAddress Is Nothing OrElse _
_EmailAddress IsNot value Then
Dim propertyName As String = "EmailAddress"
' Perform any validation
If _EmailAddress IsNot value Then
_EmailAddress = value
SetEntityState(EntityStateType.Modified, _
propertyName)
End If
End If
End Set
End Property
End Class
The first if statement in each of the property procedures for the Customer class checks whether the current value is null or was changed. If the property is changed, you want to revalidate it, set it as modified, and generate a PropertyChanged event (which is handled in the SetEntityState method).
NOTE: C# uses a != operator to judge whether the value was changed. VB cannot use the <> operator because VB propagates null values. So if either value is null (nothing) the result is false. (See this msdn entry for more information.) To prevent this problem, IsNot is used in the VB code to determine if the value was changed.
The null check allows for possible validation of required fields (that is, fields that cannot be null or empty). If you know you will never need a null validation check, you can leave the null check off the first if statement.
The propertyName variable defines a name that you can use in any validation error messages and it is the name used in the PropertyChanged event.
You can then perform any necessary validation. In this simple example, no validation was added. If you want to see an example of some validation, check out this post.
Finally, if the value was changed, the backing variable is set to the new value and the SetEntityState method is called to mark the business object as "dirty" and to generate the PropertyChanged event.
Define the Testing Scenarios
So let's start with the test of the LastName property. Looking at the requirements, the following testing scenarios are required:
- Initial null value; set to null value (should perform validation but not set the dirty flag)
- Initial null value; set to valid string (should perform validation and set the dirty flag)
- Initial null value; set to empty string (should perform validation and set the dirty flag)
- Initial string value; set to null value (should perform validation and set the dirty flag)
- Initial string value, set to different string value (should perform validation and set the dirty flag)
- Initial string value, set to same string value (should not perform validation and not set the dirty flag)
- Initial string value, set to empty value (should perform validation and set the dirty flag)
And if you have validation code, you will have more scenarios to test valid and invalid values. But this is enough to give you the general idea.
So now let's generate the unit test for the LastName property using the tools provided in Visual Studio (Professional Edition or above).
Generate the Unit Test
The following are the steps for generating the unit test for a particular property procedure or method:
1) Open the Code Editor for the code you want to test.
2) Right-click and select Create Unit Tests... from the context menu.
The following dialog will appear:
3) Select the properties, methods, or constructors you wish to test.
If you were in a specific property procedure or method, that procedure or method is automatically checked in this dialog. You can select to generate tests for any properties, methods, or constructors using this dialog.
Notice the Output project combobox on this dialog. Using this combobox, you can create a new C# unit test, new VB unit test, or select any existing unit test project if you already have some in your solution. This allows you to add unit tests to existing unit testing projects at any point in the development process.
4) Click OK.
5) If you are creating a new C# or VB project, enter the project name and click Create.
6) The test project is then added to your Solution Explorer.
If you created a VB test project, the result is the same, but with a CustomerTest.vb file within the BoTest project.
View the Generated Code
Visual Studio automatically creates an first cut of your unit testing code.
In C#:
Double-click on CustomerTest.cs to view the code.
/// <summary>
///A test for LastName
///</summary>
[TestMethod()]
public void LastNameTest()
{
Customer target = new Customer(); // TODO: Initialize to an appropriate value
string expected = string.Empty; // TODO: Initialize to an appropriate value
string actual;
target.LastName = expected;
actual = target.LastName;
Assert.AreEqual(expected, actual);
Assert.Inconclusive("Verify the correctness of this test method.");
}
In VB:
Double-click on CustomerTest.vb to view the code.
'''<summary>
'''A test for LastName
'''</summary>
<TestMethod()> _
Public Sub LastNameTest()
Dim target As Customer = New Customer ' TODO: Initialize to an appropriate value
Dim expected As String = String.Empty ' TODO: Initialize to an appropriate value
Dim actual As String
target.LastName = expected
actual = target.LastName
Assert.AreEqual(expected, actual)
Assert.Inconclusive("Verify the correctness of this test method.")
End Sub
The TestMethod attribute defines this method as a unit test. It is then picked up by the other unit testing tools within Visual Studio.
The generated code includes some ToDo notes, defining where you should change the code.
The Assert class is a key aspect of your unit tests. It provides many methods that you can use to assert whether your test produced expected results.
The generated code uses two of the Assert class methods. The AreEqual method determines whether the expected and actual values are equal. If not, it fails the test.
The Inconclusive method always causes the test to fail. This is added to all generated code to ensure that the test will fail until you modify the test with valid values.
At this point, you can try to execute this unit test, or update it first and then execute it.
See the following posts for more information:
Enjoy!
If you are using XML in a WinForms application you may find the need to display the XML data in a DataGridView.
Let's take this XML:
<states>
<state name="California">
<abbreviation>CA</abbreviation>
<year>1850</year>
<governor>Schwarzenegger</governor>
</state>
<state name="Wisconsin">
<abbreviation>WI</abbreviation>
<year>1848</year>
<governor>Doyle</governor>
</state>
</states>
Displaying XML in a DataGridView sounds easy, but if you just set the DataGridView DataSource to the XML data, you will get something like this:
Notice how it has lots of yuck in it: attribute details, node properties, and so on for every node in the XML. So how do you get something more like this:
The trick is to use anonymous types.
The code is provided here in VB and C# and then described in detail below.
NOTE: Be sure to set a reference to System.Core and System.Xml.Linq
In C#:
XElement statesXml = XElement.Parse("<states>" +
"<state name='California'>" +
"<abbreviation>CA</abbreviation>" +
"<year>1850</year>" +
"<governor>Schwarzenegger</governor>" +
"</state>" +
"<state name='Wisconsin'>" +
"<abbreviation>WI</abbreviation>" +
"<year>1848</year>" +
"<governor>Doyle</governor>" +
"</state>" +
"</states>");
var query = from st in statesXml.Descendants("state")
select new
{
Name = st.Attribute("name").Value,
Abbrev = st.Element("abbreviation").Value,
Year = st.Element("year").Value,
Governor = st.Element("governor").Value
};
DataGridView1.DataSource = query.ToList();
In VB:
Dim statesXml As XElement = _
<states>
<state name="California">
<abbreviation>CA</abbreviation>
<year>1850</year>
<governor>Schwarzenegger</governor>
</state>
<state name="Wisconsin">
<abbreviation>WI</abbreviation>
<year>1848</year>
<governor>Doyle</governor>
</state>
</states>
Dim query = From st In statesXml...<state> _
Select New With { _
.Name = st.@name, _
.Abbrev = st.<abbreviation>.Value, _
.Year = st.<year>.Value, _
.Governor = st.<governor>.Value}
DataGridView1.DataSource = query.ToList
The first part of this code builds the XML. The C# code uses the XElement.Parse function to build the XML; VB uses XML literals. This part of the code is not necessary if you are reading the XML from another source, such as a file.
The second part of the code leverages Linq to XML to process the set of state XML elements. For each element, it uses the Select New syntax to create an anonymous type. The syntax defines an unnamed (anonymous) type with properties Name, Abbrev, Year, and Governor.
[To view an overview of anonymous types, start here.]
The last line converts the results of the query to a generic list and assigns it to the DataSource property of the DataGridView. Visual Studio uses the anonymous type property names as the text for the column titles and populates the rows with each state element.
Use this technique any time you have XML that you want to display in a DataGridView.
Enjoy!
Lambda expressions can be assigned to a delegate variable. The lambda expression is then executed when the delegate is called.
[To begin with an overview of lambda expressions, start here.]
In the following example, a lambda expression is assigned to a variable (f).
In C#:
Func<int,string> f = x => (x + x).ToString();
Debug.WriteLine(f(5));
Debug.WriteLine(f(10));
In VB:
Dim f = Function(x as Integer) (x + x).ToString()
Debug.WriteLine(f(5))
Debug.WriteLine(f(10))
The lambda expression in this example is a Func<int, string> [Func(Of Integer, String) in VB] meaning it takes one integer input parameter and returns a string. The lambda expression simply takes the input value, adds it to itself, and returns the result as a string.
[For more information on Func delegates, see this post.]
When the expression is defined, it is not executed. It is not executed until it is called.
The first Debug.WriteLine statement calls the lambda expression, passing in a 5. The result displays "10" in the debug window.
The second Debug.WriteLine statement calls the lambda expression again, passing in a 10. The result displays "20" in the debug window.
Lambda expressions are executed when they are called, not when they are constructed. This is important to consider, especially when the lambda expression contains local variables.
Local variables used in a lambda expression are “captured” or “lifted”. The variable value used is the value at execution time. The variable lifetime extends to the lifetime of the delegate.
The following code updates the original example to use a local variable.
In C#:
int y = 0;
Func<int,string> f = x => (x + y).ToString();
y = 10;
Debug.WriteLine(f(5));
In VB:
Dim y As Integer = 0
Dim f = Function(x) (x + y).ToString()
y = 10
Debug.WriteLine(f(5))
The lambda expression in this example takes the input value, adds it to the current value of the local variable (y), and returns the result as a string.
The Debug.WriteLine statement in this example calls the lambda expression, passing in a 5. At the point of executing the Debug statement, y is 10, so the result displays "15" in the debug window.
For another example of using lambda expressions with local variables, see this post.
Enjoy!
The same several questions often come up in the forums regarding the basics of building a user control with VB.NET or C#. The goal of this post is to answer those questions.
There are three basic types of WinForms user controls that you can create:
- Extended control
- Composite control
- Custom control
Each of these are discussed below.
Extended Control
Use an extended control whenever you want to extend the behavior of one particular control. Say for example that you want to build your own TextBox that only allows alphabetic characters. Or you want to build your own Button that has specific behaviors.
The best thing about extended controls is that you automatically get all of the intrinsic behavior of the original control. All you need to do is add any extended functionality that you want for the control.
To create this type of user control, follow these steps.
In C#:
- Right-click on the project and select Add | User Control.
- Name the user control and click Add.
- Do NOT put anything on the design surface of the user control.
- Open the code behind file (UserControl1.cs).
- Modify the code to inherit from the control you are extending.
public partial class UserControl1 : TextBox
This example extends a TextBox control. Replace TextBox above with whatever WinForm control you want to extend.
NOTE: After changing the Inherits statement, you may get an error on a line that begins with this.AutoScaleMode. If so, just delete that line. It is no longer needed now that you are inheriting from a control other than UserControl.
In VB:
- Right-click on the project and select Add | User Control.
- Name the user control and click Add.
- Do NOT put anything on the design surface of the user control.
- Select the project in the Solution Explorer and click the Show All Files button in the Solution Explorer toolbar.
- Click on the plus sign to the left of the user control in Solution Explorer to view the nodes below it.
- Double-click on the designer file to open it. (UserControl1.Designer.vb)
- Modify the code to inherit from the control you are extending.
Partial Class UserControl1
Inherits TextBox
This example extends a TextBox control. Replace TextBox above with whatever WinForm control you want to extend.
NOTE: After changing the Inherits statement, you may get an error on a line that begins with Me.AutoScaleMode. If so, just delete that line. It is no longer needed now that you are inheriting from a control other than UserControl.
Then add any additional behavior or functionality that you want in the extended control.
The MSDN documentation provides an example of building an extended control using VB.NET here and C# here.
Composite Control
A composite controls is so named because it is composed of multiple controls. Use this type of user control any time you want to build a control that combines a set of other controls. For example, you want to build a search control that contains a Textbox for entry of the text to search, Button to click to perform the search, and ListBox for the results of the search.
The best thing about composite controls is that you can easily add any controls to the composite control using the design surface.
However, because it is a composite control that inherits from UserControl, no properties, methods, or events from any of the underlying controls are exposed. This means that when you use the user control, you cannot set the Text property of the TextBox or respond to Button click events without writing some code.
You basically have to write code to expose any property, method, or event that you need.
To create this type of user control, follow these steps:
- Right-click on the project and select Add | User Control.
- Name the user control and click Add.
- Put the controls you want onto the design surface.
- Expose any properties, methods, or events that you need.
An example of generating events from a user control is provided in this post.
The MSDN documentation provides an example of building a composite control using VB.NET here and C# here.
Custom Control
A custom control is a control you create from the ground up including all control drawing and any unique behavior.
To build a custom control, inherit from Control to get the basic behavior of a WinForms control. Then use the OnPaint event to render your custom user interface.
The MSDN documentation provides an example of building a simple custom control here.
Enjoy!
A Func delegate encapsulates a method that returns a value. It takes up to four parameters (and this number is increased in .NET 4.0) plus the return value.
[To begin with an overview of lambda expressions, start here.]
The following signature is a Func delegate that takes two integer parameters and returns a string.
In C#:
Func<int, int, string>
In VB:
Func(Of Integer, Integer, String)
There are many examples of Func delegates.
The following code demonstrates the Sum function that sums the sales total for all customers in the list (and assumes that a Customer object has a SalesTotal property).
In C#:
var total = custList.Sum(c =>
c.SalesTotal);
In VB:
Dim total = custList.Sum(Function(c) _
c.SalesTotal)
Enjoy!
Just as the name implies, an action delegate encapsulates a method that performs an action and has no return value. It takes up to four parameters (and this number is increased in .NET 4.0).
[To begin with an overview of lambda expressions, start here.]
The ForEach method of the List<T> is an example of an action delegate. It takes an action delegate as a parameter.
One of the common uses of ForEach is to display all of the elements in a list using one line of code.
In C#:
custList.ForEach(c =>
Debug.WriteLine(c.LastName));
In VB:
custList.ForEach(AddressOf WriteToDebug)
NOTE: VB lambda expressions do not currently support action delegates. In the above example, the code uses a named method as the action delegate. VB 10 lambda expressions will support action delegates using the Sub keyword instead of the Function keyword.
Enjoy!
According to Wikipedia, a predicate is “a function which returns a Boolean value”.
[To begin with an overview of lambda expressions, start here.]
A predicate delegate encapsulates a method that evaluates to True or False. It takes a single parameter.
The Array class Find method is an example of a predicate delegate. It takes an array as a first parameter and a predicate delegate as its second parameter.
In C#:
var foundCustomer = Array.Find(custArray, c =>
c.LastName.StartsWith("K"));
In VB:
Dim foundCustomer = Array.Find(custArray, Function(c) _
c.LastName.StartsWith("K"))
This code iterates through the array, checking each entry and evaluating the expression as true or false. If the expression is false, it continues. If the expression is true, it returns the entry. Basically, this code finds the first customer in the array with a last name that starts with the defined letter.
Enjoy!
This post covers the syntax for lambda expressions. It uses the customer list defined in this prior post.
[To begin with an overview of lambda expressions, start here.]
The first example below uses the FirstOrDefault extension method on the Enumerable object to find the first customer that matches the defined expression. The FirstOrDefault method takes a delegate as a parameter and that delegate is defined with a lambda expression.
In C#:
' General syntax:
Customer foundCustomer =
custList.FirstOrDefault((Customer c) =>
c.CustomerId == 4);)
' With inferred typing:
var foundCustomer =
custList.FirstOrDefault(c => c.CustomerId == 4);)
The lambda expression in this example is as follows:
c => c.CustomerId == 4
The code begins with the set of parameters to the lambda expression. In this example, there is one parameter (c). The => is the “goes to” or lambda operator. The remainder of the code is the expression itself. In this case, checking for the item in the list where CustomerId is 4.
In VB:
' General syntax:
Dim foundCustomer as Customer = _
custList.FirstOrDefault(Function(c as Customer) _
c.CustomerId = 4)
' With inferred typing:
Dim foundCustomer = _
custList.FirstOrDefault(Function(c) _
c.CustomerId = 4)
The lambda expression in this example is as follows:
Function(c) c.CustomerId = 4
The code begins with the word “Function” along with the set of parameters to the lambda expression. In this example, there is one parameter (c). The remainder of the code is the expression itself. In this case, checking the item in the list where CustomerId is 4.
In either language, notice the shorter syntax with inferred typing. The compiler recognizes that the list contains customers, so it can infer the type of the parameter and the type of return value.
You can combine some of the extension methods to form more complex statements.
In C#:
var query = custList.FindAll(c =>
c.LastName.StartsWith(“K”)).OrderBy(c => c.FirstName));
In VB:
Dim query = custList.FindAll(Function(c) _
c.LastName.StartsWith(“K”)). _
OrderBy(Function(c) c.FirstName))
This code finds all of the entries that match the first lambda expression, basically all customers with a last name that begins with “K”. The code then performs an OrderBy using the second lambda expression, basically sorting the resulting list by first name.
The above examples demonstrate single-line lambdas. You can also use multi-line lambdas as shown in the example below.
In C#:
var foundCustomer =
custList.FirstOrDefault(c =>
{
Debug.WriteLine(c.LastName);
if (c.CustomerId == 4)
return true;
else
return false;
});
In VB:
Not available in VB9, coming in VB10.
Multi-line lambda expressions require the standard {} syntax to define the statement block. Whereas single-line lambda expressions automatically handle the return value for you, you have to handle it yourself in multi-line lambda expressions.
Enjoy!
A delegate is an object that holds a reference to a method with a particular parameter list and return type.
It is basically like an object-oriented, type-safe function pointer.
Delegates basically make it possible to treat methods as entities. You can then assign a delegate to a variable or pass it as a parameter.
The classic delegate example uses events.
In C#:
HelloButton.Click += new EventHandler(HelloButton_Click);
Or the short-cut version of this code:
HelloButton.Click += HelloButton_Click;
This passes the HelloButton_Click method to a new EventHandler delegate.
In VB:
AddHandler HelloButton.Click, AddressOf HelloButton_Click
In VB, the AddressOf assigns the address of the HelloButton_Click method to the delegate.
Instead of using a named method for the delegate (in this example, a HelloButton_Click method), you can use a lambda expression.
In C#:
HelloButton.Click += (s, ev) =>
MessageBox.Show("Hello World");
In VB:
AddHandler HelloButton.Click, Function(s, ev) _
MessageBox.Show("Hello World")
In this case the lambda expression has two parameters: s (sender) and ev (eventArgs). When the Click event is generated on the HelloButton, the code displays the MessageBox.
You can use a lambda expression (inline function) anywhere a delegate is required. However, if the function requires more than a few lines of code, a named method is the preferred and recommended technique.
Enjoy!
In this prior post, I detailed how to build lists of things using a generic List<T>. Once you have a list, you may need to find items in the list. There are several ways to accomplish this, but using lambda expressions is the most concise.
[To begin with an overview of lambda expressions, start here.]
For example, here is how you find an item in a generic list using a for/each statement.
In C#:
Customer foundCustomer = null;
foreach (var c in custList)
{
if (c.CustomerId == 4)
{
foundCustomer = c;
break;
}
}
Debug.WriteLine(foundCustomer);
In VB:
Dim foundCustomer As Customer = Nothing
For Each c As Customer In custList
If c.CustomerId = 4 Then
foundCustomer = c
Exit For
End If
Next
Debug.WriteLine(foundCustomer)
Notice the amount of code required to loop through each item in the list and find the one with a particular customer Id.
You may have heard about language integrated query (LINQ) and how you can use it to search a list.
In C#:
Customer foundCustomer = null;
var query = from c in custList
where c.CustomerId == 4
select c;
foundCustomer = query.FirstOrDefault();
Debug.WriteLine(foundCustomer);
In VB:
Dim foundCustomer As Customer = Nothing
Dim query = From c As Customer In custList _
Where c.CustomerId = 4 _
Select c
foundCustomer = query.FirstOrDefault()
Debug.WriteLine(foundCustomer)
So LINQ is cool, but the code does not seem much shorter.
Now let’s look at using lambda expressions to find the item in the list.
In C#:
Customer foundCustomer = null;
foundCustomer = custList.FirstOrDefault(c =>
c.CustomerId == 4);
Debug.WriteLine(foundCustomer); The lambda expression syntax in C# looks like this:
c => c.CustomerId == 4
The code begins with the set of parameters to the lambda expression. The => is the “goes to” or lambda operator. The remainder of the code is the expression itself. In this case, checking for the item in the list where CustomerId is 4.
In VB:
Dim foundCustomer As Customer = Nothing
foundCustomer = custList.FirstOrDefault(Function(c)
c.CustomerId = 4)
Debug.WriteLine(foundCustomer)
The lambda expression syntax in VB looks like this:
Function(c) c.CustomerId = 4
The code begins with the word “Function” along with the set of parameters to the lambda expression. The remainder of the code is the expression itself. In this case, checking the item in the list where CustomerId is 4.
.NET 3.5 added a large list of extension methods on the Enumerable class. Any object that implements IEnumerable or IEnumerable<T> (basically any object you can do a for/each over) can use these methods. Many of these extension methods (such as the FirstOrDefault method shown in the above example) support lambda expressions.
The FirstOrDefault extension method of the Enumerable class returns the first item in the list or the default value for the object. If you pass it a lambda expression, it returns the first item in the list that matches the Boolean expression.
Enjoy!
My presentation at our local Code Camp was fun. I covered a lot of material and had a GREAT audience. Several people have asked for the slide information in my blog. So this is the start of several posts on lambda expressions.
In addition, I wrote an article on lambda expressions that should appear in the Jan/Feb 2010 issue of Code Magazine. I’ll post the link as soon as its published.
Lambda expressions are unnamed, inline functions. They are new in Visual Basic 9 and C# 3.0 (VS 2008). You can use a lambda expression anywhere a delegate is required. For more information on delegates, see this post.
Here are an additional set of posts that cover the slides and information from my talk:
Enjoy!
The Silicon Valley Code Camp is coming up tomorrow (October 3, 2009). I am looking forward to attending!
My presentation is on Lambda expressions in C# and VB. You can find the code here.
If you are interested in finding out more about this FREE code camp, check it out here.
Enjoy!