Introduction
This article presents a possibility to create a generics conversion method which can be used by the basic types (bool
, int
, byte
, etc.).
Background
There are cases where you want to create generic processing for several different data types which are very similar but don't have a common parent class or don't inherit a common interface. This is very often the case with the basic .NET classes that represent the value types bool
, int
, byte
, etc. Although they are all value types, they don't have a common parent that has any useful methods.
A simple example given here: you want to provide the user with several text boxes where he/she can enter a value that needs to be tested. This is very easy to do by just coding a different method for each value type like this:
private void TestInt32(TextBox textBox, Label resultLabel)
{
try
{
Int32 res = Convert.ToInt32(textBox.Text);
resultLabel.Text = "Success!";
resultLabel.BackColor = Color.Green;
}
catch
{
resultLabel.Text = "Failure!";
resultLabel.BackColor = Color.Red;
}
}
private void TestBool(TextBox textBox, Label resultLabel)
{
try
{
Boolean res = Convert.ToBoolean(textBox.Text);
resultLabel.Text = "Success!";
resultLabel.BackColor = Color.Green;
}
catch
{
resultLabel.Text = "Failure!";
resultLabel.BackColor = Color.Red;
}
}
This is both annoying and repeating a lot of code to do the same. Unfortunately, Microsoft did not provide us with a way to write a generics class/method for the basic value types. This article shows a possible solution.
Using the Code
The example code shows a generics method for conversion of an object to a provided basic value type.
public static T ToT<T>(object value)
{
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Boolean:
return (T)(object)Convert.ToBoolean(value);
case TypeCode.Byte:
return (T)(object)Convert.ToByte(value);
case TypeCode.Char:
return (T)(object)Convert.ToChar(value);
case TypeCode.DateTime:
return (T)(object)Convert.ToDateTime(value);
case TypeCode.Decimal:
return (T)(object)Convert.ToDecimal(value);
case TypeCode.Double:
return (T)(object)Convert.ToDouble(value);
case TypeCode.Int16:
return (T)(object)Convert.ToInt16(value);
case TypeCode.Int32:
return (T)(object)Convert.ToInt32(value);
case TypeCode.Int64:
return (T)(object)Convert.ToInt64(value);
case TypeCode.Object:
return (T)value;
case TypeCode.SByte:
return (T)(object)Convert.ToSByte(value);
case TypeCode.Single:
return (T)(object)Convert.ToSingle(value);
case TypeCode.String:
return (T)(object)Convert.ToString(value);
case TypeCode.UInt16:
return (T)(object)Convert.ToUInt16(value);
case TypeCode.UInt32:
return (T)(object)Convert.ToUInt32(value);
case TypeCode.UInt64:
return (T)(object)Convert.ToUInt64(value);
case TypeCode.DBNull:
case TypeCode.Empty:
default:
throw new ApplicationException("Unsupported generics type");
}
}
The double conversion to object
and then to T
in every return is unfortunately unavoidable because Visual Studio cannot identify at compile time that the result of the conversion is of type T
.
Thus a single method can be used to verify all our fields:
private void Test<T>(TextBox textBox, Label resultLabel)
{
try
{
T res = ConvertEx.ToT<T>(textBox.Text);
resultLabel.Text = "Success!";
resultLabel.BackColor = Color.Green;
}
catch
{
resultLabel.Text = "Failure!";
resultLabel.BackColor = Color.Red;
}
}
private void testButton_Click(object sender, EventArgs e)
{
Test<Int32>(this.int32TextBox, this.int32ResultLabel);
Test<Boolean>(this.boolTextBox, this.boolResultLabel);
Test<Decimal>(this.decimalTextBox, this.decimalResultLabel);
}
Further Development
Conversion is not the only operation that one would need for basic value types. For example, general Parse
or TryParse
methods would be very helpful and are pretty easy to implement in the same way.
Gratitude
I would like to specially thank Dobby who helped me with the consideration of this method and helped me make it clearer and easier to understand.