Melhor maneira de testar se um tipo genérico é uma string? (C #)

93

Tenho uma classe genérica que deve permitir qualquer tipo, primitivo ou não. O único problema com isso é usar default(T). Quando você chama default em um tipo de valor ou string, ele o inicializa com um valor razoável (como string vazia). Quando você chama default(T)um objeto, ele retorna nulo. Por vários motivos, precisamos garantir que, se não for um tipo primitivo, teremos uma instância padrão do tipo, não nula. Aqui está a tentativa 1:

T createDefault()
{
    if(typeof(T).IsValueType)
    {
        return default(T);
    }
    else
    {
        return Activator.CreateInstance<T>();
    }
}

Problema - string não é um tipo de valor, mas não tem um construtor sem parâmetros. Portanto, a solução atual é:

T createDefault()
{
    if(typeof(T).IsValueType || typeof(T).FullName == "System.String")
    {
        return default(T);
    }
    else
    {
        return Activator.CreateInstance<T>();
    }
}

Mas isso parece um desastre. Existe uma maneira mais agradável de lidar com o caso da string?

Rex M
fonte

Respostas:

161

Lembre-se de que o padrão (string) é nulo, não string.Empty. Você pode querer um caso especial em seu código:

if (typeof(T) == typeof(String)) return (T)(object)String.Empty;
Matt Hamilton
fonte
2
Pensei ter tentado essa solução antes e não funcionou, mas devo ter feito algo estúpido. E obrigado por apontar que default (string) retorna nulo, não encontramos um erro ainda por causa disso, mas é verdade.
Rex M
1
@Matt Hamilton: +1, mas você deve atualizar sua resposta para retornar '(T) (objeto) String.Empty' como sugerido por CodeInChaos porque o tipo de retorno do método é genérico, você não pode apenas retornar string.
VoodooChild
2
E quanto à ispalavra - chave? Isso não é útil aqui?
Naveed Butt
No momento não é possível aplicar o operador is com genéricos e atribuição ou instanciação direta, não é ?, será um recurso legal
Juan Pablo Garcia Coello
14
if (typeof(T).IsValueType || typeof(T) == typeof(String))
{
     return default(T);
}
else
{
     return Activator.CreateInstance<T>();
}

Não testado, mas a primeira coisa que me veio à mente.

FlySwat
fonte
4

Você pode usar a enumeração TypeCode . Chame o método GetTypeCode em classes que implementam a interface IConvertible para obter o código de tipo para uma instância dessa classe. IConvertible é implementado por Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char e String, então você pode verificar por tipos primitivos usando isso. Mais informações sobre " Verificação de tipo genérico ".

jfs
fonte
2

Pessoalmente, gosto de sobrecarga de método:

public static class Extensions { 
  public static String Blank(this String me) {      
    return String.Empty;
  }
  public static T Blank<T>(this T me) {      
    var tot = typeof(T);
    return tot.IsValueType
      ? default(T)
      : (T)Activator.CreateInstance(tot)
      ;
  }
}
class Program {
  static void Main(string[] args) {
    Object o = null;
    String s = null;
    int i = 6;
    Console.WriteLine(o.Blank()); //"System.Object"
    Console.WriteLine(s.Blank()); //""
    Console.WriteLine(i.Blank()); //"0"
    Console.ReadKey();
  }
}
Theoski
fonte
-6

A discussão sobre String não está funcionando aqui.

Eu precisava seguir o código de genéricos para fazê-lo funcionar -

   private T createDefault()
    { 

        {     
            if(typeof(T).IsValueType)     
            {         
                return default(T);     
            }
            else if (typeof(T).Name == "String")
            {
                return (T)Convert.ChangeType(String.Empty,typeof(T));
            }
            else
            {
                return Activator.CreateInstance<T>();
            } 
        } 

    }
Anil
fonte
3
Testar Stringpor nome, especialmente sem considerar um namespace, é ruim. E também não gosto da maneira como você se converte.
CodesInChaos