Maybe the reason it's not there is because delimited strings can be error-prone, and serialization mechanisms support arrays. But let's forget about why, because sometimes you need to do this.
I write a few simple, yet flexible methods for accomplishing this. I used arrays, although another version of the methods could be created for IEnumerables or ICollections or whatever. For my situation, I needed only arrays so I looked past my abstraction apprehensions and just did it.
One interesting thing was that in certain situations I needed custom code to convert each item to and from its string. So my methods allow for that using a delegate. And to accomodate for all array types and for type safety, I used generic methods. So this is a cute way to blend those two features that have been around since C# 2.0.
/// <summary>
/// Takes a delimited string and returns an array of items that were converted to the type specified. Individual value conversions
/// are done by using the type converter ConvertFromInvariantString method.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="separator"></param>
/// <returns></returns>
public static T[] ConvertDelimitedStringToArray<T>(string value, string separator)
{
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
Func<T, string> itemConversionHandler = delegate(string itemText)
{
return (T)converter.ConvertFromInvariantString(itemText);
};
return ConvertDelimitedStringToArray(value, separator, itemConversionHandler);
}
/// <summary>
/// Takes a delimited string and returns an array of items that were converted to the type specified.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="separator"></param>
/// <param name="itemConversionHandler">The delegate that converts each string item into the target type.</param>
/// <returns></returns>
public static T[] ConvertDelimitedStringToArray<T>(string value, string separator, Func<T,string> itemConversionHandler)
{
if (string.IsNullOrEmpty(value))
return null;
string[] pieces = value.Split(new string[]{separator},StringSplitOptions.None);
T[] result = new T[pieces.Length];
for (int i = 0; i < pieces.Length; i++)
{
result[i] = itemConversionHandler(pieces[i]);
}
return result;
}
/// <summary>
/// Takes an array of items and converts it to a delimited string with the separator. Individual value conversions
/// are done by using the type converter ConvertToInvariantString method.
/// </summary>
public static string ConvertArrayToDelimitedString<T>(T[] value, string separator)
{
TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
Func<string, T> itemConversionHandler = delegate(T item)
{
return converter.ConvertToInvariantString(item);
};
return ConvertArrayToDelimitedString(value, separator, itemConversionHandler);
}
/// <summary>
/// Takes an array of items and converts it to a delimited string with the separator and conversion handler provided.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="value"></param>
/// <param name="separator"></param>
/// <param name="itemConversionHandler">The delegate that converts each item into a piece of text.</param>
/// <returns></returns>
public static string ConvertArrayToDelimitedString<T>(T[] value, string separator, Func<string,T> itemConversionHandler)
{
if (value == null || value.Length == 0)
return null;
string[] textPieces = new string[value.Length];
for (int i = 0; i < value.Length; i++)
{
textPieces[i] = itemConversionHandler(value[i]);
}
return string.Join(separator, textPieces);
}
If you have .NET 3.5 (I think), the Func delegate is already there. If not, you can just define it yourself:
/// <summary>
/// A delegate to a methd that returns some TReturn value and has 1 parameter.
/// </summary>
/// <typeparam name="TReturn">The type of object the method returns.</typeparam>
/// <typeparam name="TParam1">The type of the first method parameter.</typeparam>
/// <returns></returns>
internal delegate TReturn Func<TReturn, TParam1>(TParam1 param1);
That's all you need, really. To demonstrate, check out the code below:
//create some ints and make them pipe separated
int[] ids = new int[] { 1, 2, 3, 4 };
string delimitedList = ConvertArrayToDelimitedString<int>(ids, "|");
ids = ConvertDelimitedStringToArray(delimitedList, "|");//and back again
//now I take some dates and put only their day in a list
DateTime[] dates = new DateTime[] { DateTime.Parse("2/29/1980"), DateTime.Parse("3/1/1980"), DateTime.Parse("3/5/1998") };
//my own 'custom' (albeit useless) conversion code to convert each date to have its parts separated by -
delimitedList = ConvertArrayToDelimitedString<DateTime>(dates, "|",
delegate(DateTime item)
{
return item.ToString("MM-DD-YY");//forgive me if my format string is not right
}//note: I could have declared the delegate in a variable above, but kept it here to confuse people and hopefully make them think I am some kind of wizard (don't you love guys like that?)
);
No comments:
Post a Comment