I am trying to use the LINQ to CSV project in silverlight (its a great project), because its open sourced i figured i could just recompile as a silverlight class library but unfortunately it appears to use a feature not available in silverlight. The TypeDescriptor.GetConverter method.
It uses this to find type converters to properly parse the csv columns to their corresponding CLR types. I have no problem making changes to the linqtocsv sources to make it work in Silverlight, but i just don't know what an equivalent operation would be in silverlight. Various google searches have brought me to this page, but all that says is that the XAML 开发者_运维问答parser has a way of doing this (but it doesn't say how to access this functionality).
In a nutshell, the question is:
how do i replicate the functionality of TypeDescriptor.GetConverter
?
I don't necessarily need an exact drop in replacement, i just want to know a good way to do this without hardcoding a bunch of type <---> typeconverter associations.
On the light frameworks you have limited options; I wouldn't shy away from a little hard-coding, especially if you only need to support the core types. It will also be simpler and faster than the full TypeConverter
option. Something like:
static object Parse(Type type, string s)
{
switch (Type.GetTypeCode(type))
{
case TypeCode.Boolean: return bool.Parse(s);
case TypeCode.Byte: return byte.Parse(s);
case TypeCode.Char: return s[0];
case TypeCode.DateTime: return DateTime.Parse(s);
...
}
}
I have built a rather comprehensive toolset to solve this issue. There are many steps involved but here it is:
1) Return the original value if the target type is Assignable from the original value 2) Construct a type converter otherwise (here come the interesting parts)
public static TypeConverter GetTypeConverter(Type type)
{
TypeConverterAttribute attribute = (TypeConverterAttribute)Attribute.GetCustomAttribute(type, typeof(TypeConverterAttribute), false);
if (attribute != null)
{
try
{
var converterType = Type.GetType(attribute.ConverterTypeName, false);
if (converterType != null)
{
return (Activator.CreateInstance(converterType) as TypeConverter);
}
}
catch {}
}
return new XamlStringConverter(type);
}
Nothing marvellous here. But notice the XamlStringConverter returned if no converter was found. It makes use of the Xaml Parser to convert things. The type field is included in the Converter, and contains the type passed to the constructor.
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
var strValue = value as string;
if (strValue != null) {
if (this.type == typeof(bool)) {
return bool.Parse(strValue);
}
if (this.type.IsEnum) {
return Enum.Parse(this.type, stringValue, false);
}
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("<ContentControl xmlns='http://schemas.microsoft.com/client/2007' xmlns:c='" + ("clr-namespace:" + this.type.Namespace + ";assembly=" + this.type.Assembly.FullName.Split(new char[] { ',' })[0]) + "'>\n");
stringBuilder.Append("<c:" + this.type.Name + ">\n");
stringBuilder.Append(strValue);
stringBuilder.Append("</c:" + this.type.Name + ">\n");
stringBuilder.Append("</ContentControl>");
ContentControl instance = XamlReader.Load(stringBuilder.ToString()) as ContentControl;
if (instance != null) {
return instance.Content;
}
}
return base.ConvertFrom(context, culture, value);
}
If you're sure that you can rely on consistency, use this:
private static object DeserializeValue(string value, Type type)
{
//uncomment if used with XML
//value = HttpUtility.HtmlDecode(value);
switch (Type.GetTypeCode(type))
{
case TypeCode.Empty:
return null;
case TypeCode.DBNull:
return DBNull.Value;
case TypeCode.Object:
throw new InvalidCastException(
string.Format("The type '{0}' is not supported.", type));
case TypeCode.String:
return value;
default:
{
var convertible = value as IConvertible;
return convertible.ToType(type, CultureInfo.InvariantCulture);
}
}
}
Make sure your serialization function is same culture as the deserialization for consistency:
private static string SerializeValue(object value)
{
if (!(value is IConvertible))
throw new InvalidCastException(
string.Format("The type '{0}' is not supported.", value.GetType()));
var convertible = (IConvertible)value;
var str = convertible.ToString(CultureInfo.InvariantCulture);
//uncomment if you're serializing to XML
//return HttpUtility.HtmlEncode(str);
return str;
}
Note that only primitive types are supported.
From: http://lostechies.com/jimmybogard/2010/02/19/automapper-for-silverlight-3-0-alpha/
private static TypeConverter GetTypeConverter(Type type)
{
var attributes = type.GetCustomAttributes(typeof(TypeConverterAttribute), false);
if (attributes.Length != 1)
return new TypeConverter();
var converterAttribute = (TypeConverterAttribute)attributes[0];
var converterType = Type.GetType(converterAttribute.ConverterTypeName);
if (converterType == null)
return new TypeConverter();
return Activator.CreateInstance(converterType) as TypeConverter;
}
精彩评论