Using Generics in an Extension Method

If you’ve followed any of my posts you’ll know that I get a lot of posts from problems that I face and the solutions that I come up with. This post is no exception. I ran into an issue trying to create an extension method using generics. (e.g System.Nullable<T> or System.Nullable(Of T))

I had a value coming back from a LINQ to SQL stored proc call that was defined as a nullable type (System.Nullable(Of Byte)). When I went to set the .Text property of one of my textboxes I was presented with this error: “Nullable object must have value”
Textbox1.Text = myObject.Property.Value ‘error occurs here

Ok. I’ve seen that error before. But why am I getting it now? A quick google search and I quickly remembered that you can’t use the “value” method of a nullable type if the “value” is null. Weird but that’s the behavior.

I obviously wanted to fix this problem so I started off by writing this code:
TextBox1.Text = If (myValue.HasValue, myValue.Value.ToString(), string.Empty)

That was ok but I would have had to repeat the same code all of the place in my project. I’m not a big fan of copy / paste. I’d rather write a method to do this. My next thought was to write an extension method to handle the conversion. That way I can simply write something like:
TextBox1.Text = myValue.ToNullableString()

However, I only wanted to write one extension method. I COULD write a bunch of extension methods for each type that I would ever return from the db (i.e. byte, int32, Int16, etc). Some of those would look like this:

C#
public static string ToNullableString(this System.Nullable<Int32> param) 
{
    // implementation here
}

public static string ToNullableString(this System.Nullable<byte> param) {     // implementation here }

VB
Public Function ToNullableString(ByVal param As System.Nullable(Of Int32)) As String
‘ implementation here
End Function

Public Function ToNullableString(ByVal param As System.Nullable(Of Byte)) As String ‘ implementation here End Function

Well I certainly don't want to write a method for each type that can be return from the database. I only wanted to write ONE method. That means I’m going to creating a generic method.  I thought “Can I even write an extension method for a generic type?”. Come to find out that you can. It just takes a little more work.

As you SHOULD already know (if not, click here) the first parameter in an extension method is the type being extended. For this extension method I needed the parameter to be of type System.Nullable(Of T). My first iteration of the method looked like this:

C#
public static string ToNullableString(this System.Nullable<T> param) 
{
//implementation
}

VB
Public Function ToNullableString(ByVal param As Nullable(Of T)) As String 
‘ implementation
End Function

But with this version I received an error stating “Type T is not defined” in VB and “The type or namespace name 'T' could not be found (are you missing a using directive or an assembly reference?” in C#.

Make sense, I haven’t defined what “T” should be yet. Next I tried this:

VB
Public Function ToNullableString(Of T)(ByVal param As Nullable(Of T)) As String
End Function
C#
public static string ToNullableString<T>(this System.Nullable<T> param) 
{
//implementation
}
Well, that didn’t work either. This time I received this error in VB “Type 'T' must be a value type or a type argument constrained to 'Structure' in order to be used with 'Nullable' or nullable modifier '?'.” and this error “The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable<T>'”  in C#.

After reviewing my Pro C# book I figured out what the solution was. I needed to use the “where” constraint. In our case the constraint will be for “struct” or “Structure”. This tells the compiler to only allow this method to be used by types that have System.ValueType in its chain of inheritance. These are the final iterations of the method:

C#
public static string ToNullableString<T>(this System.Nullable<T> param) where T : struct 
{
	if (param.HasValue)
	{
		return param.Value.ToString();
	}
	else
	{
		return string.Empty;
	}
}

VB
<Extension()> _
Public Function ToNullableString(Of T As Structure)(ByVal param As Nullable(Of T)) As String
	If (Not param.HasValue) Then
 		Return String.Empty
	Else
 		Return param.Value.ToString
 	End If
End Function

You'll notice that the "where" constraint is defined as "As Structure" in vb.

So now I can use the code like this:
Dim myValue As System.Nullable(Of Integer) =  GetValueFromLinqToSql()
TextBox1.Text = myValue.ToNullableString() ‘ now we don’t get an error!


Hope this helps anyone else that has run into a similar problem.

posted @ Wednesday, April 02, 2008 10:40 PM

Print

Comments on this entry:

# re: Using Generics in an Extension Method

Left by Brent at 4/23/2008 5:21 PM
Gravatar
Thanks Chris. This worked well. I found that when I attempt to use this to populate a date textbox, however, the function appends 12:00:00 AM as it returns the param.value.tostring. I created another function

<Extension()> _
Public Function ToNullableDate(ByVal param As Nullable(Of Date)) As Date
If (Not param.HasValue) Then
Return Nothing
Else
Return param.Value
End If
End Function

to get around it, copying one of your examples. Would there be a cleaner way to implement this as a generic method?

# re: Using Generics in an Extension Method

Left by Chris Rock at 4/24/2008 11:16 AM
Gravatar
Good question brent. I will do a little poking around and see what I find.

Chris

# re: Using Generics in an Extension Method

Left by Daniel at 9/28/2008 3:37 AM
Gravatar
I have a problem using this generic method when I try to use it for string type, because string is not a struct.
So, when if call MyGenericMethod<string>("abc")
I get this error again.
What can I do?
Thanks, Daniel

# re: Using Generics in an Extension Method

Left by Andy at 6/29/2009 2:23 PM
Gravatar
You don't have to return a string. Return whatever type you need:

public static Nullable<T> ConvertToNullable<T>(object value) where T : struct
{
Type conversionType = typeof(T);
return (T)Convert.ChangeType(value, conversionType);
}

Your comment:



 (will not be displayed)


 
 
 
Please add 2 and 7 and type the answer here:
 

Live Comment Preview:

 
«March»
SunMonTueWedThuFriSat
28123456
78910111213
14151617181920
21222324252627
28293031123
45678910