Le meilleur moyen de tester si un type générique est une chaîne? (C #)

93

J'ai une classe générique qui devrait autoriser n'importe quel type, primitif ou autre. Le seul problème avec cela est d'utiliser default(T). Lorsque vous appelez default sur un type valeur ou une chaîne, il l'initialise à une valeur raisonnable (telle qu'une chaîne vide). Lorsque vous appelez default(T)un objet, il renvoie null. Pour diverses raisons, nous devons nous assurer que s'il ne s'agit pas d'un type primitif, nous aurons une instance par défaut du type, non null. Voici la tentative 1:

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

Problème - la chaîne n'est pas un type valeur, mais elle n'a pas de constructeur sans paramètre. Donc, la solution actuelle est:

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

Mais cela ressemble à un kludge. Existe-t-il une meilleure façon de gérer la casse des chaînes?

Rex M
la source

Réponses:

161

Gardez à l'esprit que la valeur par défaut (chaîne) est null, pas une chaîne. Vous voudrez peut-être un cas particulier dans votre code:

if (typeof(T) == typeof(String)) return (T)(object)String.Empty;
Matt Hamilton
la source
2
Je pensais avoir essayé cette solution plus tôt et cela n'a pas fonctionné, mais j'ai dû faire quelque chose de stupide. Et merci d'avoir signalé que default (string) renvoie null, nous n'avons pas encore rencontré d'erreur à cause de cela, mais c'est vrai.
Rex M
1
@Matt Hamilton: +1, mais vous devez mettre à jour votre réponse pour renvoyer '(T) (object) String.Empty' comme suggéré par CodeInChaos car le type de retour de la méthode est générique, vous ne pouvez pas simplement renvoyer une chaîne.
VoodooChild
2
Et le ismot-clé? N'est-ce pas utile ici?
Naveed Butt
Pour le moment, il n'est pas possible d'appliquer l'opérateur is avec des génériques et une affectation ou une instanciation directe, n'est-ce pas?, Sera une fonctionnalité intéressante
Juan Pablo Garcia Coello
14
if (typeof(T).IsValueType || typeof(T) == typeof(String))
{
     return default(T);
}
else
{
     return Activator.CreateInstance<T>();
}

Non testé, mais la première chose qui m'est venue à l'esprit.

FlySwat
la source
4

Vous pouvez utiliser l' énumération TypeCode . Appelez la méthode GetTypeCode sur les classes qui implémentent l'interface IConvertible pour obtenir le code de type d'une instance de cette classe. IConvertible est implémenté par Boolean, SByte, Byte, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Decimal, DateTime, Char et String, afin que vous puissiez vérifier les types primitifs à l'aide de this. Plus d'informations sur " Vérification du type générique ".

jfs
la source
2

Personnellement, j'aime la surcharge de méthodes:

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
la source
-6

La discussion pour String ne fonctionne pas ici.

Je devais avoir le code suivant pour les génériques pour que cela fonctionne -

   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
la source
3
Tester Stringpar nom, surtout sans tenir compte d'un espace de noms, est mauvais. Et je n'aime pas non plus la façon dont vous convertissez.
CodesInChaos