Как я могу исправить это, чтобы сделать общее преобразование в Nullable <T>?
В настоящее время я использую этот удобный метод расширения для преобразования между типами:
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]]'.
Это, очевидно, упрощенный пример, на самом деле я использую его для преобразования из строковых типов:
packageDb.Quantity = package.package.ElementDeep(Namespace + "PackageQuantity", Namespace + "ActualQuantity", Namespace + "Quantity").ValueOrNull().To<int?>();
Если Convert.ChangeType не нравится Nullable, у кого-нибудь есть отличные идеи?
Ответы
Ответ 1
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);
}
}
Ответ 2
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);
}
Но если преобразование завершится неудачно, оно выкинет исключение, а не вернет нуль, как и следовало ожидать.
Ответ 3
Я закончил с этим
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)
при ошибке.
Ответ 4
Возможно, мне не хватает точки, но в случае с Nullable, как ваш метод обеспечивает преимущество чтения, производительности или обслуживания перед простым приведением, например (int?)1
?
Кроме того, возможно, другой метод расширения?
public static T? ToNullable<T>(this T obj) where T:struct
{
return (T?)obj;
}
Изменить
После просмотра вашего редактирования, почему общая функция, которую я предоставил, не работает в качестве замены вашей функции To<T>
в этой строке кода? Вы не можете разрешить преобразование в Nullable для любого типа (поэтому ChangeType
не работает), потому что этот общий тип принимает только типы значений. Вам нужно будет использовать функцию, подобную той, которую я предоставил, или изменить вашу подпись To<T>
, чтобы принимать только типы значений и добавлять специальный случай для Nullable<T>
.
Ответ 5
Решение Люка было хорошо для меня (и, очевидно, получил его голос), но я упростил это для меня таким образом
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;
}
потому что я начал с строки не из типа...
мысли?
Ответ 6
Это метод, который я использую в настоящее время (я получил свой ответ на fooobar.com/questions/29221/...), он преобразует из строки в тип NULL:
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;
}
Ответ 7
продлить код @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);
}
}
Ответ 8
Этот метод делает то, что вам нужно, и при этом он выглядит хорошо.
/// <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
Источник