Типа в качестве универсального параметра возможные значения null?
Я хочу сделать что-то вроде этого :
myYear = record.GetValueOrNull<int?>("myYear"),
обратите внимание на тип nullable в качестве универсального параметра.
С GetValueOrNull функция может возвращать null, моя первая попытка была такой:
public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : class
{
object columnValue = reader[columnName];
if (!(columnValue is DBNull))
{
return (T)columnValue;
}
return null;
}
но ошибка, которую я получаю сейчас:
тип ' int?'должен быть ссылочным типом, чтобы использовать его в качестве параметра 'T' в универсальном типе или методе
правильно! Nullable<int> это struct! Поэтому я попытался изменить ограничение класса к struct ограничение (и как побочный эффект не может вернуть null больше):
public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct
теперь задание:
myYear = record.GetValueOrNull<int?>("myYear");
выдает следующую ошибку:
тип ' int?'должен быть ненулевым типом значения, чтобы использовать его в качестве параметра 'T' в универсальном типе или методе
возможно ли вообще указывать тип nullable в качестве общего параметра?
10 ответов:
измените тип возвращаемого значения на Nullable и вызовите метод с параметром nullable
static void Main(string[] args) { int? i = GetValueOrNull<int>(null, string.Empty); } public static Nullable<T> GetValueOrNull<T>(DbDataRecord reader, string columnName) where T : struct { object columnValue = reader[columnName]; if (!(columnValue is DBNull)) return (T)columnValue; return null; }
public static T GetValueOrDefault<T>(this IDataRecord rdr, int index) { object val = rdr[index]; if (!(val is DBNull)) return (T)val; return default(T); }просто используйте его так:
decimal? Quantity = rdr.GetValueOrDefault<decimal?>(1); string Unit = rdr.GetValueOrDefault<string>(2);
просто сделайте две вещи для вашего исходного кода-удалите
whereограничение, и изменить последнийreturnСreturn nullдоreturn default(T). Таким образом, вы можете вернуть любой тип, который вы хотите.кстати, вы можете избежать использования
isпутем измененияifзаявлениеif (columnValue != DBNull.Value).
просто нужно было сделать что-то невероятное, похожее на это. Мой код:
public T IsNull<T>(this object value, T nullAlterative) { if(value != DBNull.Value) { Type type = typeof(T); if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition()) { type = Nullable.GetUnderlyingType(type); } return (T)(type.IsEnum ? Enum.ToObject(type, Convert.ToInt32(value)) : Convert.ChangeType(value, type)); } else return nullAlternative; }
отказ от ответственности: этот ответ работает, но предназначен только для образовательных целей. :) Решение Джеймса Джонса, вероятно, лучшее здесь и, конечно, тот, с которым я бы пошел.
в C# 4.0 л!--4--> ключевое слово делает это еще проще, если менее безопасный:
public static dynamic GetNullableValue(this IDataRecord record, string columnName) { var val = reader[columnName]; return (val == DBNull.Value ? null : val); }теперь вам не нужен явный тип, намекающий на RHS:
int? value = myDataReader.GetNullableValue("MyColumnName");на самом деле, вам даже не нужно это вообще!
var value = myDataReader.GetNullableValue("MyColumnName");
valueтеперь будет int, или строка, или какой бы тип не был возвращен из БД.единственная проблема заключается в том, что это не мешает вам использовать ненулевые типы на LHS, и в этом случае вы получите довольно неприятное исключение времени выполнения, например:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to 'int' because it is a non-nullable value typeкак и со всем кодом, который использует
dynamic: нюанс верстальщик.
Я думаю, вы хотите обрабатывать ссылочные типы и типы структур. Я использую его для преобразования строк XML-элементов в более типизированный тип. Вы можете удалить nullAlternative с отражением. В formatprovider обработки зависит от культуры.'или ',' разделитель, например, десятичных дробей или целых чисел и парном разрядах. Это может сработать:
public T GetValueOrNull<T>(string strElementNameToSearchFor, IFormatProvider provider = null ) { IFormatProvider theProvider = provider == null ? Provider : provider; XElement elm = GetUniqueXElement(strElementNameToSearchFor); if (elm == null) { object o = Activator.CreateInstance(typeof(T)); return (T)o; } else { try { Type type = typeof(T); if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>).GetGenericTypeDefinition()) { type = Nullable.GetUnderlyingType(type); } return (T)Convert.ChangeType(elm.Value, type, theProvider); } catch (Exception) { object o = Activator.CreateInstance(typeof(T)); return (T)o; } } }Вы можете использовать его как это:
iRes = helper.GetValueOrNull<int?>("top_overrun_length"); Assert.AreEqual(100, iRes); decimal? dRes = helper.GetValueOrNull<decimal?>("top_overrun_bend_degrees"); Assert.AreEqual(new Decimal(10.1), dRes); String strRes = helper.GetValueOrNull<String>("top_overrun_bend_degrees"); Assert.AreEqual("10.1", strRes);
Это может быть мертвый поток, но я склонен использовать следующее:
public static T? GetValueOrNull<T>(this DbDataRecord reader, string columnName) where T : struct { return reader[columnName] as T?; }
Я только что столкнулся с той же проблемой сам.
... = reader["myYear"] as int?;работает и чисто.Он работает с любым типом без вопрос. Если результатом является значение dbnull, он возвращает значение null, как преобразование завершается неудачей.
Я знаю, что это старый, но вот еще одно решение:
public static bool GetValueOrDefault<T>(this SqlDataReader Reader, string ColumnName, out T Result) { try { object ColumnValue = Reader[ColumnName]; Result = (ColumnValue!=null && ColumnValue != DBNull.Value) ? (T)ColumnValue : default(T); return ColumnValue!=null && ColumnValue != DBNull.Value; } catch { // Possibly an invalid cast? return false; } }теперь вам все равно, если
Tбыло значения или ссылочный тип. Только если функция возвращает true, у вас есть разумное значение из базы данных. Использование:... decimal Quantity; if (rdr.GetValueOrDefault<decimal>("YourColumnName", out Quantity)) { // Do something with Quantity }этот подход очень похож на
int.TryParse("123", out MyInt);
несколько общих ограничений не могут быть объединены способом или (менее ограничительным), только способом и (более ограничительным). Это означает, что один метод не может обрабатывать оба сценария. Общие ограничения также не могут быть использованы для создания уникальной подписи для метода, поэтому вам придется использовать 2 отдельных имени метода.
однако вы можете использовать общие ограничения, чтобы убедиться, что методы используются правильно.
в моем случае я специально хотел, чтобы null был возвращается, и никогда значение по умолчанию для любых возможных типов значений. GetValueOrDefault = плохо. GetValueOrNull = хорошо.
я использовал слова "Null" и "Nullable", чтобы различать ссылочные типы и типы значений. И вот пример пары методов расширения я написал, что комплименты FirstOrDefault метод в системе.В LINQ.Перечисляемый класс.
public static TSource FirstOrNull<TSource>(this IEnumerable<TSource> source) where TSource: class { if (source == null) return null; var result = source.FirstOrDefault(); // Default for a class is null return result; } public static TSource? FirstOrNullable<TSource>(this IEnumerable<TSource?> source) where TSource : struct { if (source == null) return null; var result = source.FirstOrDefault(); // Default for a nullable is null return result; }
Comments