Comment faire ToString pour un objet éventuellement nul?

97

Existe-t-il un moyen simple de faire ce qui suit:

String s = myObj == null ? "" : myObj.ToString();

Je sais que je peux faire ce qui suit, mais je le considère vraiment comme un hack:

String s = "" + myObj;

Ce serait formidable si Convert.ToString () avait une surcharge appropriée pour cela.

codymanix
la source
3
Je ne vois rien de mal avec le premier. Si vous considérez le second comme un hack, le mieux est d'écrire simplement une fonction utilitaire qui effectue la vérification nulle.
Nick Larsen
plz pouvez-vous être plus précis sur votre question
FosterZ
3
string.Format ("{0}", myObj) accepte les valeurs nulles.
Holstebroe
3
Avec C # 6.0, nous pouvons maintenant utiliser des opérateurs conditionnels nuls, comme theText? .ToString () ou theText? .Trim ()
Santosh
2
Par cette réponse Convert.ToString() fait exactement la première chose que vous avez écrite en dessous.
drzaus

Réponses:

175

Édition C # 6.0:

Avec C # 6.0, nous pouvons maintenant avoir une version succincte et sans cast de la méthode orignal:

string s = myObj?.ToString() ?? "";

Ou même en utilisant l'interpolation:

string s = $"{myObj}";

Réponse originale:

String s = (myObj ?? String.Empty).ToString();

ou

String s = (myObjc ?? "").ToString()

pour être encore plus concis.

Malheureusement, comme cela a été souligné, vous aurez souvent besoin d'un cast de chaque côté pour que cela fonctionne avec des types non String ou Object:

String s = (myObjc ?? (Object)"").ToString()
String s = ((Object)myObjc ?? "").ToString()

Par conséquent, même s'il semble élégant, le casting est presque toujours nécessaire et n'est pas si succinct dans la pratique.

Comme suggéré ailleurs, je recommande peut-être d'utiliser une méthode d'extension pour rendre ce nettoyeur:

public static string ToStringNullSafe(this object value)
{
    return (value ?? string.Empty).ToString();
}
Andrew Hanlon
la source
Cela compilera-t-il? L'opérateur de fusion ne vérifie-t-il pas les types?
Nick Larsen
1
Cela fonctionne puisque (object ?? string) renvoie object, car coalesce utilise le type le moins courant. Mais je pense que cela ne fonctionnera pas pour les interfaces car il ne peut pas décider quelle interface choisir (puisque plusieurs interfaces sont autorisées par classe).
codymanix
1
Pas vraiment la solution que j'espérais mais je vais l'accepter
codymanix
4
Le vote négatif en tant que cet objet est terriblement moche et implique de la boxe.
steinar
1
la première option de c # 6 est très élégante
Alexandre N.
40
string.Format("{0}", myObj);

string.Format formatera null comme une chaîne vide et appellera ToString () sur les objets non nuls. Si je comprends bien, c'est ce que vous recherchiez.

Holstebroe
la source
3
Personnellement, je n'aime pas ça. Le code peut être insignifiant plus court que String s = myObj == null? "": myObj.ToString (), mais vous cachez totalement le raisonnement derrière votre méthode.
AGuyCalledGerald
Certes, c'est de la microoptimisation, mais cela prend environ 5 fois plus de temps que juste "" + myObj. Mais j'ai lu que cela crée des chaînes supplémentaires. str.Concat(myObj)semble fonctionner très bien et est "encore plus rapide".
drzaus
18

Ce serait formidable si Convert.ToString () avait une surcharge appropriée pour cela.

Il y a eu Convert.ToString(Object value)depuis .Net 2.0 (environ 5 ans avant que cette question ne soit posée), qui semble faire exactement ce que vous voulez:

http://msdn.microsoft.com/en-us/library/astxcyeh(v=vs.80).aspx

Est-ce que je manque / interprète mal quelque chose de vraiment évident ici?

Rob Gilliam
la source
1
Vous ne le faites pas, ils le font. Pourquoi simple quand cela peut être compliqué :)
alex.peter
13

Avec une méthode d'extension, vous pouvez accomplir ceci:

public static class Extension
{
    public static string ToStringOrEmpty(this Object value)
    {
        return value == null ? "" : value.ToString();
    }
}

Ce qui suit n'écrirait rien à l'écran et ne lèverait pas d'exception:

        string value = null;

        Console.WriteLine(value.ToStringOrEmpty());
Pieter van Ginkel
la source
Les appels de méthode d'extension omettent-ils la vérification habituelle des valeurs nulles ...?
Matti Virkkunen
2
Il ajoute que vous n'avez pas à taper "{0}" et que vous pouvez choisir un nom de méthode qui décrit ce que vous faites. Ceci est particulièrement utile lorsque vous faites cela plusieurs fois. Vous pouvez même nommer la méthode ToStringOrEmptyWhenNull.
Pieter van Ginkel
2
Si l'OP pense que string s = "" + myObj;c'est piraté, l'appel d'une fonction sur un objet nul devrait tomber dans ce même bateau. Je voterais contre cela, mais cela résout le problème, je ne suis simplement pas d'accord avec l'utilisation. Les objets nuls devraient lancer NullReferenceException, même dans les méthodes d'extension.
Nick Larsen
2
@NickLarsen: Je suis d'accord avec vous dans la plupart des cas, mais parfois vous voulez juste la commodité d'un simple appel de méthode. Le nom de la méthode devrait vous indiquer que les références nulles sont OK, comme avec la proposition pour ToStringOrEmptyWhenNull.
Jordão
3
Les méthodes d'extension ne lancent pas le "premier" paramètre (le thisparamètre) car elles ne sont que du sucre syntaxique. C'est x.SomeExtensionMethod()du sucre syntaxique pour SomeStaticClass.SomeExtensionMethod(x);Ainsi, quand xest-ce que nullnous n'essayons pas d'invoquer une méthode sur un nullobjet mais plutôt de passer un nullobjet à une méthode statique. Si et seulement si cette méthode vérifie un nullparamètre et lance lors de la rencontre, une méthode d'extension "invoquée" sur un nullobjet sera lancée.
jason
7

Je ne suis pas d'accord avec cela:

String s = myObj == null ? "" : myObj.ToString();

est un hack en aucune façon. Je pense que c'est un bon exemple de code clair. Il est absolument évident que vous voulez réaliser et que vous attendez null.

METTRE À JOUR:

Je vois maintenant que vous ne disiez pas que c'était un hack. Mais cela implique dans la question que vous pensez que cette voie n'est pas la voie à suivre. Dans mon esprit, c'est certainement la solution la plus claire.

steinar
la source
Il n'a pas dit que cette méthode était un piratage. En fait, il n'a pas dit ce qui n'allait pas, sauf peut-être sous-entendu que ce n'était pas simple. Je ne pense pas qu'il y ait quelque chose de mal avec cette méthode. +1 parce que je le ferais de cette façon.
Mark Byers
Je suis d'accord avec OP ... il y a déjà un opérateur de fusion nul dans c # - voir mon article ci-dessous.
Andrew Hanlon
Je pense que l'opérateur de fusion nul est un peu moins clair dans ce cas, mais cela me conviendrait tout aussi bien de l'utiliser.
steinar
@Pieter Très bien. Mais c'est le cas. La question est "Y a-t-il un moyen simple de faire ce qui suit: ...". Cette manière exacte est simple!
steinar
Je n'ai jamais dit que cette méthode était un hack. Veuillez relire ma question. Il ne me manque qu'une petite fonction dans le Framework qui a cette fonctionnalité.
codymanix
4
string s = String.Concat(myObj);

serait le moyen le plus court que je suppose et aurait également une surcharge de performance négligeable. Gardez à l'esprit qu'il ne serait pas tout à fait clair pour le lecteur du code quelle est l'intention.

herzmeister
la source
2
C'est la forme explicite de "" + myObj mais encore pire pour dire ce qui se passe ici. Intéressant en tout cas.
codymanix
@codymanix je ne pense pas que ce soit la même chose - Concatfait en fait une vérification nulle en dessous et retourne string.Emptyou arg0.ToString(), ce qui semble être légèrement plus performant (je veux dire, nous parlons de ms ici).
drzaus
2

en fait, je n'ai pas compris ce que vous voulez faire. Si je comprends bien, vous pouvez écrire ce code d'une autre manière comme celle-ci. Le demandez-vous ou non? Pouvez-vous expliquer plus?

string s = string.Empty;
    if(!string.IsNullOrEmpty(myObj))
    {
    s = myObj.ToString();
    }
Serkan Hekimoglu
la source
Bien sûr, je peux l'écrire comme ça mais je veux un simple petit appel de fonction, je me demandais pourquoi il n'y a rien de tel dans le .NET BCL
codymanix
Avez-vous vraiment besoin d'une fonction alors que nous avons la méthode IsNullOrEmpty ()?
Serkan Hekimoglu
Sur cette note, j'ai toujours voulu que l'opérateur de fusion échoue ensuite une fois qu'il atteint une référence nulle dans votre expression courante, mais je comprends parfaitement pourquoi c'est une idée terrible. Un remplacement qui vous permet de le faire serait bien aussi.
Nick Larsen
1
string s = string.IsNullOrEmpty(myObj) ? string.Empty : myObj.ToString();
Nick Larsen
2

Je pourrais être battu pour ma réponse, mais voici quand même:

J'écrirais simplement

string s = "" if (myObj! = null) {x = myObj.toString (); }

Y a-t-il un gain en termes de performances pour l'utilisation de l'opérateur ternaire? Je ne sais pas par-dessus ma tête.

Et clairement, comme quelqu'un l'a mentionné ci-dessus, vous pouvez mettre ce comportement dans une méthode telle que safeString (myObj) qui permet la réutilisation.

Jaydel
la source
OMG si l'objet est nul, alors appelez ToString () dessus?
codymanix
Allez, c'est une faute de frappe. J'ai remplacé le premier = par! pour le rendre correct. Mais tu vas vraiment me demander une faute de frappe?
jaydel
2

J'ai eu le même problème et je l'ai résolu en jetant simplement l'objet en chaîne. Cela fonctionne également pour les objets nuls car les chaînes peuvent être nulles. À moins que vous ne souhaitiez absolument pas avoir de chaîne nulle, cela devrait fonctionner correctement:

string myStr = (string)myObj; // string in a object disguise or a null
jahu
la source
La solution la plus courte et la meilleure. (Obj1 ?? "") .ToString () lance également Obj1 en tant que chaîne :)
TPAKTOPA
2

Quelques tests de performance (de vitesse) résumant les différentes options, pas que cela compte vraiment #microoptimization (en utilisant une extension linqpad )

Options

void Main()
{
    object objValue = null;
    test(objValue);
    string strValue = null;
    test(strValue);
}

// Define other methods and classes here
void test(string value) {
    new Perf<string> {
        { "coallesce", n => (value ?? string.Empty).ToString() },
        { "nullcheck", n => value == null ? string.Empty : value.ToString() },
        { "str.Format", n => string.Format("{0}", value) },
        { "str.Concat", n => string.Concat(value) },
        { "string +", n => "" + value },
        { "Convert", n => Convert.ToString(value) },
    }.Vs();
}

void test(object value) {
    new Perf<string> {
        { "coallesce", n => (value ?? string.Empty).ToString() },
        { "nullcheck", n => value == null ? string.Empty : value.ToString() },
        { "str.Format", n => string.Format("{0}", value) },
        { "str.Concat", n => string.Concat(value) },
        { "string +", n => "" + value },
        { "Convert", n => Convert.ToString(value) },
    }.Vs();
}

Il est probablement important de souligner qu'il Convert.ToString(...)conservera une chaîne nulle.

Résultats

Objet

  • nullcheck 1,00 x 1221 ticks écoulés (0,1221 ms) [en 10 000 répétitions, 1,221E-05 ms par]
  • coallesce 1,14x 1387 ticks écoulés (0,1387 ms) [en 10K répétitions, 1,387E-05 ms par]
  • chaîne + 1,16 x 1415 ticks écoulés (0,1415 ms) [en 10 000 répétitions, 1,415E-05 ms par]
  • str.Concat 1,16x 1420 ticks écoulés (0,142 ms) [en 10K répétitions, 1,42E-05 ms par]
  • Convertir 1,58x 1931 ticks écoulés (0,1931 ms) [en 10K répétitions, 1,931E-05 ms par]
  • str.Format 5,45x 6655 ticks écoulés (0,6655 ms) [en 10K reps, 6.655E-05 ms par]

Chaîne

  • nullcheck 1,00 x 1190 ticks écoulés (0,119 ms) [en 10 000 répétitions, 1,19E-05 ms par]
  • Convertir 1,01 x 1200 ticks écoulés (0,12 ms) [en 10 000 répétitions, 1,2E-05 ms par]
  • chaîne + 1,04x 1239 ticks écoulés (0,1239 ms) [en 10K répétitions, 1,239E-05 ms par]
  • coallesce 1,20x 1423 ticks écoulés (0,1423 ms) [en 10K reps, 1,423E-05 ms par]
  • str.Concat 4,57x 5444 ticks écoulés (0,5444 ms) [en 10K reps, 5.444E-05 ms par]
  • str.Format 5,67 x 6750 ticks écoulés (0,675 ms) [en 10K répétitions, 6,75E-05 ms par]
drzaus
la source
1

Le commentaire de Holstebroe serait votre meilleure réponse:

string s = string.Format("{0}", myObj);

Si myObj est null, Format y place une valeur de chaîne vide.

Il répond également à votre exigence d'une ligne et est facile à lire.

jp2code
la source
oui, mais le code n'est pas clair. Combien de vos collègues développeurs sauront ce que vous essayez d'accomplir?
AGuyCalledGerald
0

Même si c'est une vieille question et que l'OP a demandé C #, j'aimerais partager une solution VB.Net pour ceux qui travaillent avec VB.Net plutôt que C #:

Dim myObj As Object = Nothing
Dim s As String = If(myObj, "").ToString()

myObj = 42
s = If(myObj, "").ToString()

Malheureusement, VB.Net n'autorise pas l'opérateur? Après une variable, donc myObj? .ToString n'est pas valide (du moins pas dans .Net 4.5, que j'ai utilisé pour tester la solution). Au lieu de cela, j'utilise If pour renvoyer une chaîne vide au cas où myObj ist Nothing. Ainsi, le premier Tostring-Call renvoie une chaîne vide, tandis que le second (où myObj n'est pas Nothing) renvoie "42".

Sascha
la source