Ask Kathleen

Utilize Constraints in Custom Generic Classes

Learn how constraints work when creating custom generic classes; display the VB splash screen for an extended period of time; and preserve Handles clauses when you cut-and-paste them.

Technology Toolbox: VB .NET, C#

Q I'm going to be creating some custom generic classes, but I don't understand the concept of constraints. I've heard that you need constraints to create new objects in the generic class. Can you explain this?

A Assume you create a generic class like this one:

public class BizObjectList<T> :List<T>
{ // No additional Funtionality

You don't need constraints in this case because it doesn't matter what T is; you aren't calling any methods or properties of T. T can literally be of any type.

If, however, you want to work with the methods and properties of T, you need to let the compiler know what type to expect, so you can ensure these properties or methods are available. A generic list derived from System.Collections.Generic.List or System.Collections.ObjectModel.Collection that has no constraint can access only methods and properties of Object. This means it can do little more than make a call ToString().

Constraints specify that it is legal only to use the type parameters that meet the constraint, which guarantees to the compiler that the methods and properties of the constraint are accessible:

public class BizList<T> : List<T> 
	where T : BizBase<T>
	public void Save()
		foreach (T item in this)
		{ item.Save(); }

You can call the Save method because it exists on the BizBase<T> class, which is a constraint (Listing 1). The C# syntax defines constraints with the where clause at the end of the class or method declaration. The Visual Basic syntax is a little different:

Public Class BizList(Of T As BizBase( _
	Of T))
	Inherits List(Of T)

	Public Sub Save()
		For Each item As T In Me
	End Sub
End Class

The two most important types of constraints are base class constraints and interface constraints. In both cases, the constraint allows all methods and properties available in scope. You can include combinations of a base class and interfaces.

There is a third type of constraint that isn't all that useful. In order to create a new instance of the type parameter, the compiler needs a guarantee that a constructor is available. Unfortunately, Microsoft made two mistakes in this implementation. Instead of checking for the constructor in scope, it requires that the constructor be public. Expanding the scope of your constructors to use them in generics is not acceptable. The constructor also has to be parameterless. If you're using parameterized constructors to ensure your objects are immediately in a valid state, you're not going to abandon this best practice to use parameterless constructors.

Instead, you must use a little reflection to create your objects.

dim newT as T
newT = TryCast( _
	Activator.CreateInstance(gettype(T)), T)

TryCast is equivalent to the as operator in C# (see the question, "What does TryCast do?" in VSM's June 2007 issue [Q&A, "Whip WPF Snippets Into Shape"]). This code is messier than using new, but you don't have to redesign your constructors. Overloads of the CreateInstance method let you access private constructors and include parameters as an array. Using this method to access private constructors enforces a factory pattern where new objects are created only through this code. Parameters are passed as Object and will be coerced to the correct data type. You can ensure this coercion is based on your tested language if you pass InvariantCulture as the CultureInfo parameter.

Q I want to display a splash screen for my Visual Basic application. I can get it to display, but it disappears too quickly. How do I make it show longer?

A The Visual Basic infrastructure displays a splash screen automatically if you provide one in the Visual Basic project options. The length of time that it displays is one of several infrastructure settings that you can alter by overriding the OnInitialize method (or by handling the Initialize event):

Imports System.Collections.ObjectModel
Protected Overrides Function OnInitialize( _
	ByVal commandLineArgs As _
	ReadOnlyCollection(Of String)) _
	As Boolean
	' Set the display time to 5 seconds 
	Me.MinimumSplashScreenDisplayTime = 5000
	Return MyBase.OnInitialize(commandLineArgs)
End Function

This works for WinForms in Visual Studio 2005, but the application infrastructure settings aren't available for WPF in Visual Studio 2005.

Q I work in Visual Basic, and I like the declarative approach of the Handles clause for events. But I find it frustrating that the Handles clauses are removed when I cut-and-paste controls, especially when I'm just moving them between tabs. Is there a way to preserve the Handles clauses when I cut-and-paste them?

A Visual Studio includes a great tool for viewing the way your forms use containers for nesting; the tool is called the Document Outline. It presents a treeview of all the containers and controls on your form. If you drag controls between containers in the Document Outline, the controls are never removed from the form and thus Visual Studio doesn't remove the Handles clauses. You can find the Document Outline in the main Visual Studio menu in View|Other Windows|Document Outline.

About the Author

Kathleen is a consultant, author, trainer and speaker. She’s been a Microsoft MVP for 10 years and is an active member of the INETA Speaker’s Bureau where she receives high marks for her talks. She wrote "Code Generation in Microsoft .NET" (Apress) and often speaks at industry conferences and local user groups around the U.S. Kathleen is the founder and principal of GenDotNet and continues to research code generation and metadata as well as leveraging new technologies springing forth in .NET 3.5. Her passion is helping programmers be smarter in how they develop and consume the range of new technologies, but at the end of the day, she’s a coder writing applications just like you. Reach her at [email protected]

comments powered by Disqus