我如何解决这个问题做generics转换为Nullable <T>?
我目前使用这个方便的转换扩展方法来做types之间的转换:
public static T To<T>(this IConvertible obj) { return (T)Convert.ChangeType(obj, typeof(T)); }
但是,它不喜欢将有效值转换为Nullable,例如,这将失败:
"1".To<int?>();
很显然,1很容易转换为(int?),但是却得到错误:
Invalid cast from 'System.String' to 'System.Nullable`1[[System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]'.
这是一个明显简化的例子,实际上我使用它来从stringtypes进行转换,如下所示:
packageDb.Quantity = package.package.ElementDeep(Namespace + "PackageQuantity", Namespace + "ActualQuantity", Namespace + "Quantity").ValueOrNull().To<int?>();
如果Convert.ChangeType不喜欢Nullable,任何人有任何伟大的想法?
public static T To<T>(this IConvertible obj) { Type t = typeof(T); Type u = Nullable.GetUnderlyingType(t); if (u != null) { return (obj == null) ? default(T) : (T)Convert.ChangeType(obj, u); } else { return (T)Convert.ChangeType(obj, t); } }
public static T To<T>(this IConvertible obj) { Type t = typeof(T); if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>)) t = t.GetGenericArguments()[0]; return (T)Convert.ChangeType(obj, t); }
但是,如果转换失败,它将会抛出一个exception,而不是像预期的那样返回null。
我已经结束了这一点
private static T To<T>(this Object @object, Boolean returnDefaultOnException) { Type type = typeof(T); Type underlyingTypeOfNullable = Nullable.GetUnderlyingType(type); try { return (T) Convert.ChangeType(@object, underlyingTypeOfNullable ?? type); } catch (Exception exception) { if (returnDefaultOnException) return default(T); String typeName = type.Name; if (underlyingTypeOfNullable != null) typeName += " of " + underlyingTypeOfNullable.Name; throw new InvalidCastException("Object can't be cast to " + typeName, exception); } } public static T To<T>(this Object @object) { return @object.To<T>(returnDefaultOnException: false); } public static T ToOrDefault<T>(this Object @object) { return @object.To<T>(returnDefaultOnException: true); }
它的行为像LINQ扩展方法Single
和SingleOrDefault
以及First
和FirstOrDefault
。
总之, To<T>()
尝试转换并抛出失败,而ToOrDefault<T>()
尝试转换并返回失败时的default(T)
。
也许我错过了这一点,但在Nullable的实例中,您的方法如何提供可读性,性能或维护优势,而不是简单的转换,如(int?)1
?
除此之外,也许还有另一种扩展方法?
public static T? ToNullable<T>(this T obj) where T:struct { return (T?)obj; }
编辑
在审查你的编辑之后,为什么我提供的generics函数不能作为你的To<T>
函数的替代工具在那行代码中工作? 你不能允许任何types的转换为Nullable(这就是为什么ChangeType
不起作用),因为这个generics只接受值types。 你将不得不使用一个像我提供的函数或改变你的签名To<T>
只接受值types并为Nullable<T>
添加一个特殊的情况。
卢克的解决scheme对我来说是好事(显然他的投票是有利的),但是我这样简化了它
private static Type ResolveType(String typeName) { Type t = Type.GetType(typeName); if (t == null) return null; Type u = Nullable.GetUnderlyingType(t); if (u != null) { t = u; } return t; }
因为我从一个string开始,而不是从一个types…思想?
这是我目前使用的方法(我得到了我的答案),它从string转换为可空types:
public static Nullable<T> ConvertToNullable<T>(this string s) where T : struct { if (!string.IsNullOrEmpty(s.Trim())) { TypeConverter conv = TypeDescriptor.GetConverter(typeof(Nullable<>).MakeGenericType(typeof(T))); return (Nullable<T>)conv.ConvertFrom(s); } return null; }
扩展@LukeH代码:
public static T GetValue<T>(string Literal, T DefaultValue) { if (Literal == null || Literal == "" || Literal == string.Empty) return DefaultValue; IConvertible obj = Literal; Type t = typeof(T); Type u = Nullable.GetUnderlyingType(t); if (u != null) { return (obj == null) ? DefaultValue : (T)Convert.ChangeType(obj, u); } else { return (T)Convert.ChangeType(obj, t); } }
这种方法可以做到你所需要的,而且在做这件事的时候看起来不错。
/// <summary> /// <para>More convenient than using T.TryParse(string, out T). /// Works with primitive types, structs, and enums. /// Tries to parse the string to an instance of the type specified. /// If the input cannot be parsed, null will be returned. /// </para> /// <para> /// If the value of the caller is null, null will be returned. /// So if you have "string s = null;" and then you try "s.ToNullable...", /// null will be returned. No null exception will be thrown. /// </para> /// <author>Contributed by Taylor Love (Pangamma)</author> /// </summary> /// <typeparam name="T"></typeparam> /// <param name="p_self"></param> /// <returns></returns> public static T? ToNullable<T>(this string p_self) where T : struct { if (!string.IsNullOrEmpty(p_self)) { var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)); if (converter.IsValid(p_self)) return (T)converter.ConvertFromString(p_self); if (typeof(T).IsEnum) { T t; if (Enum.TryParse<T>(p_self, out t)) return t;} } return null; }
UnitTests 来源