c# - valor - tipo de dato null
Tipo de Nullable como un parĂ¡metro genĂ©rico posible? (10)
Acabo de encontrar el mismo problema yo mismo.
... = reader["myYear"] as int?;
Funciona y está limpio.
Funciona con cualquier tipo sin problema. Si el resultado es DBNull, devuelve un valor nulo a medida que falla la conversión.
Quiero hacer algo como esto:
myYear = record.GetValueOrNull<int?>("myYear"),
Observe el tipo anulable como el parámetro genérico.
Puesto que la función GetValueOrNull
podría devolver nulo, mi primer intento fue este:
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;
}
Pero el error que estoy recibiendo ahora es:
El tipo ''int?'' debe ser un tipo de referencia para usarlo como parámetro ''T'' en el tipo o método genérico
¡Derecha! Nullable<int>
es una struct
! Así que intenté cambiar la restricción de clase a una restricción de struct
(y como efecto secundario ya no puedo devolver el null
):
public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct
Ahora la tarea:
myYear = record.GetValueOrNull<int?>("myYear");
Da el siguiente error:
El tipo ''int?'' debe ser un tipo de valor no anulable para usarlo como parámetro ''T'' en el tipo o método genérico
¿Es posible especificar un tipo anulable como un parámetro genérico?
Cambie el tipo de retorno a Nullable y llame al método con el parámetro no 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;
}
Creo que quieres manejar tipos de referencia y tipos de estructura. Lo uso para convertir cadenas de elementos XML a un tipo más escrito. Puede eliminar la alternativa nula con la reflexión. El proveedor de formato es para manejar la cultura dependiente ''''. o '','' separador en, por ejemplo, decimales o ints y dobles. Esto puede funcionar:
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;
}
}
}
Puedes usarlo así:
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);
Esto puede ser un hilo muerto, pero tiendo a usar lo siguiente:
public static T? GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct
{
return reader[columnName] as T?;
}
Las múltiples restricciones genéricas no se pueden combinar en una forma OR (menos restrictiva), solo en una forma AND (más restrictiva). Lo que significa que un método no puede manejar ambos escenarios. Las restricciones genéricas tampoco se pueden usar para hacer una firma única para el método, por lo que tendría que usar 2 nombres de métodos separados.
Sin embargo, puede usar las restricciones genéricas para asegurarse de que los métodos se usen correctamente.
En mi caso, específicamente quería que se devolviera el valor nulo, y nunca el valor predeterminado de cualquier tipo de valor posible. GetValueOrDefault = malo. GetValueOrNull = bueno.
Utilicé las palabras "Null" y "Nullable" para distinguir entre tipos de referencia y tipos de valor. Y aquí hay un ejemplo de un par de métodos de extensión que escribí que complementan el método FirstOrDefault en la clase System.Linq.Enumerable.
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;
}
Sé que esto es viejo, pero aquí hay otra solución:
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;
}
}
Ahora, no te importa si T
era valor o tipo de referencia. Solo si la función devuelve verdadero, tiene un valor razonable de la base de datos. Uso:
...
decimal Quantity;
if (rdr.GetValueOrDefault<decimal>("YourColumnName", out Quantity))
{
// Do something with Quantity
}
Este enfoque es muy similar a int.TryParse("123", out MyInt);
Solo haga dos cosas con su código original: elimine la restricción where
y cambie la última return
de return null
para return default(T)
. De esta manera podrás devolver el tipo que quieras.
Por cierto, puede evitar el uso de is
cambiando su sentencia if (columnValue != DBNull.Value)
a if (columnValue != DBNull.Value)
.
Solo tenía que hacer algo increíble similar a esto. Mi código:
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;
}
Descargo de responsabilidad: esta respuesta funciona, pero está destinada únicamente a fines educativos. :) La solución de James Jones es probablemente la mejor aquí y, ciertamente, la que elegiría.
La palabra clave dynamic
C # 4.0 hace esto aún más fácil, aunque menos seguro:
public static dynamic GetNullableValue(this IDataRecord record, string columnName)
{
var val = reader[columnName];
return (val == DBNull.Value ? null : val);
}
Ahora no necesita la sugerencia de tipo explícita en el RHS:
int? value = myDataReader.GetNullableValue("MyColumnName");
De hecho, ¡ni siquiera lo necesitas en absoluto!
var value = myDataReader.GetNullableValue("MyColumnName");
value
ahora será un int, una cadena o cualquier tipo que se haya devuelto desde la base de datos.
El único problema es que esto no le impide utilizar tipos no anulables en LHS, en cuyo caso obtendrá una excepción de tiempo de ejecución bastante desagradable como:
Microsoft.CSharp.RuntimeBinder.RuntimeBinderException: Cannot convert null to ''int'' because it is a non-nullable value type
Al igual que con todo el código que utiliza dynamic
codificador dynamic
: caveat.
public static T GetValueOrDefault<T>(this IDataRecord rdr, int index)
{
object val = rdr[index];
if (!(val is DBNull))
return (T)val;
return default(T);
}
Solo úsalo así:
decimal? Quantity = rdr.GetValueOrDefault<decimal?>(1);
string Unit = rdr.GetValueOrDefault<string>(2);