c# - use - Tipo anulável como um parâmetro genérico possível?




metodo generics c# (7)

Altere o tipo de retorno para Nullable e chame o método com o parâmetro não anulável

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;
}

Eu quero fazer algo assim:

myYear = record.GetValueOrNull<int?>("myYear"),

Observe o tipo anulável como o parâmetro genérico.

Como a função GetValueOrNull poderia retornar null, minha primeira tentativa foi esta:

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;
}

Mas o erro que estou recebendo agora é:

O tipo 'int' deve ser um tipo de referência para usá-lo como parâmetro 'T' no tipo genérico ou método

Certo! Nullable<int> é uma struct ! Então eu tentei mudar a restrição de classe para uma restrição de struct (e como um efeito colateral não pode retornar null mais):

public static T GetValueOrNull<T>(this DbDataRecord reader, string columnName)
  where T : struct

Agora a tarefa:

myYear = record.GetValueOrNull<int?>("myYear");

Dá o seguinte erro:

O tipo 'int' deve ser um tipo de valor não anulável para usá-lo como parâmetro 'T' no tipo genérico ou método

Especificar um tipo anulável como um parâmetro genérico é possível?


Apenas tive que fazer algo incrível semelhante a isso. Meu 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;
}

Eu acabei de encontrar o mesmo problema eu mesmo.

... = reader["myYear"] as int?; funciona e está limpo.

Funciona com qualquer tipo sem problema. Se o resultado for DBNull, ele retornará null quando a conversão falhar.


Eu acho que você quer lidar com tipos de referência e tipos de estrutura. Eu uso para converter seqüências de elementos XML para um tipo mais digitado. Você pode remover o nullAlternative com reflexão. O formatprovider é para lidar com a cultura dependente '.' ou ',' separador em por exemplo casas decimais ou ints e duplas. Isso pode 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; 
            }
        }
    }

Você pode usá-lo assim:

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);

Isso pode ser um thread morto, mas eu costumo usar o seguinte:

public static T? GetValueOrNull<T>(this DbDataRecord reader, string columnName)
where T : struct 
{
    return reader[columnName] as T?;
}

Várias restrições genéricas não podem ser combinadas de uma maneira OR (menos restritiva), apenas em uma forma AND (mais restritiva). O que significa que um método não pode lidar com ambos os cenários. As restrições genéricas também não podem ser usadas para criar uma assinatura exclusiva para o método, portanto, você teria que usar dois nomes de método separados.

No entanto, você pode usar as restrições genéricas para garantir que os métodos sejam usados ​​corretamente.

No meu caso, eu queria especificamente que o null fosse retornado e nunca o valor padrão de qualquer tipo de valor possível. GetValueOrDefault = ruim. GetValueOrNull = bom.

Usei as palavras "Null" e "Nullable" para distinguir entre tipos de referência e tipos de valor. E aqui está um exemplo de alguns métodos de extensão que escrevi que complementam o método FirstOrDefault na classe 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;
    }

public static T GetValueOrDefault<T>(this IDataRecord rdr, int index)
{
    object val = rdr[index];

    if (!(val is DBNull))
        return (T)val;

    return default(T);
}

Apenas use assim:

decimal? Quantity = rdr.GetValueOrDefault<decimal?>(1);
string Unit = rdr.GetValueOrDefault<string>(2);






generics