En se référant à ce qui suit réponse SE .
Lors de l'écriture
A = A ?? B;
c'est la même chose que
if( null != A )
A = A;
else
A = B;
Est-ce que ça veut dire que
if( null == A ) A = B;
serait préférable, en termes de performances?
Ou puis-je supposer que le compilateur optimise le code lorsque le même objet est dans le ??
notation?
Bien que les performances de ??
Soient négligeables, l'effet secondaire peut parfois ne pas être négligeable. Considérez le programme suivant.
using System;
using System.Diagnostics;
using System.Threading;
namespace TestProject
{
class Program
{
private string str = "xxxxxxxxxxxxxxx";
public string Str
{
get
{
return str;
}
set
{
if (str != value)
{
str = value;
}
// Do some work which take 1 second
Thread.Sleep(1000);
}
}
static void Main(string[] args)
{
var p = new Program();
var iterations = 10;
var sw = new Stopwatch();
for (int i = 0; i < iterations; i++)
{
if (i == 1) sw.Start();
if (p.Str == null)
{
p.Str = "yyyy";
}
}
sw.Stop();
var first = sw.Elapsed;
sw.Reset();
for (int i = 0; i < iterations; i++)
{
if (i == 1) sw.Start();
p.Str = p.Str ?? "yyyy";
}
sw.Stop();
var second = sw.Elapsed;
Console.WriteLine(first);
Console.WriteLine(second);
Console.Write("Ratio: ");
Console.WriteLine(second.TotalMilliseconds / first.TotalMilliseconds);
Console.ReadLine();
}
}
}
Exécutez le résultat sur mon PC.
00:00:00.0000015
00:00:08.9995480
Ratio: 5999698.66666667
Parce qu'il y a une affectation supplémentaire utilisant ??
, Et les performances de l'affectation peuvent parfois ne pas être garanties. Cela peut entraîner un problème de performances.
Je préférerais utiliser if( null == A ) A = B;
au lieu de A = A ?? B;
.
Ne vous inquiétez pas de la performance, ce sera négligeable.
Si vous en êtes curieux, écrivez du code pour tester les performances en utilisant Stopwatch
et voyez. Je suppose que vous aurez besoin de faire quelques millions d'itérations pour commencer à voir des différences.
Vous ne pouvez jamais non plus présumer de la mise en œuvre des choses, elles sont susceptibles de changer à l'avenir - annulant vos hypothèses.
Mon hypothèse est que la différence de performance est probablement très, très faible. Je choisirais l'opérateur de fusion nul pour la lisibilité personnellement, il est agréable et condensé et transmet assez bien le point. Je le fais parfois pour la vérification paresseux:
_lazyItem = _lazyItem ?? new LazyItem();
J'ai juste essayé ceci en C # - très rapidement, donc il pourrait y avoir une erreur dans ma méthode. J'ai utilisé le code suivant et j'ai déterminé que la deuxième méthode prenait environ 1,75 fois plus de temps que la première.
@ Lockszmith: Après la vérification ci-dessous, le ratio était de 1.115 en faveur de la 1ère implémentation
Même s'ils prenaient le même temps, j'utiliserais personnellement la construction de langage intégrée, car elle exprime plus clairement vos intentions à tout futur compilateur qui pourrait avoir plus d'optimisations intégrées.
@ Lockszmith: J'ai édité le code pour refléter les recommandations des commentaires
var A = new object();
var B = new object();
var iterations = 1000000000;
var sw = new Stopwatch();
for (int i = 0; i < iterations; i++)
{
if( i == 1 ) sw.Start();
if (A == null)
{
A = B;
}
}
sw.Stop();
var first = sw.Elapsed;
sw.Reset();
for (int i = 0; i < iterations; i++)
{
if( i == 1 ) sw.Start();
A = A ?? B;
}
sw.Stop();
var second = sw.Elapsed;
first.Dump();
second.Dump();
(first.TotalMilliseconds / second.TotalMilliseconds).Dump("Ratio");
Mon conseil serait d'inspecter l'IL (langage intermédiaire) et de comparer les différents résultats. Vous pouvez alors voir exactement en quoi chacun se résume et décider de ce qui est le plus optimisé. Mais comme Adam l'a dit dans son commentaire, il est probablement préférable de vous concentrer sur la lisibilité/maintenabilité plutôt que sur les performances dans quelque chose d'aussi petit.
EDIT: vous pouvez afficher l'IL en utilisant ILDASM.exe fourni avec Visual Studio et ouvrir votre Assembly compilé.
Oui, il y a une différence.
Utilisation du ciblage Visual Studio 2017 15.9.8
.NET Framework 4.6.1
. Considérez l'exemple ci-dessous.
static void Main(string[] args)
{
// Make sure our null value is not optimized away by the compiler!
var s = args.Length > 100 ? args[100] : null;
var foo = string.Empty;
var bar = string.Empty;
foo = s ?? "foo";
bar = s != null ? s : "baz";
// Do not optimize away our stuff above!
Console.WriteLine($"{foo} {bar}");
}
En utilisant ILDasm
, il devient clair que le compilateur ne traite pas ces instructions de la même manière.
?? opérateur:
IL_001c: dup
IL_001d: brtrue.s IL_0025
IL_001f: pop
IL_0020: ldstr "foo"
IL_0025: ldloc.0
Contrôle nul conditionnel:
IL_0026: brtrue.s IL_002f
IL_0028: ldstr "baz"
IL_002d: br.s IL_0030
IL_002f: ldloc.0
Apparemment, l'opérateur ??
Implique une duplication de la valeur de la pile (devrait être la variable s
, n'est-ce pas?). J'ai effectué un test simple (plusieurs fois) pour obtenir une impression dont des deux est la plus rapide. Opérant sur string
, fonctionnant sur cette machine particulière, j'ai obtenu ces nombres moyens:
?? operator took: 583 ms
null-check condition took: 1045 ms
Exemple de code de référence:
static void Main(string[] args)
{
const int loopCount = 1000000000;
var s = args.Length > 1 ? args[1] : null; // Compiler knows 's' can be null
int sum = 0;
var watch = new System.Diagnostics.Stopwatch();
watch.Start();
for (int i = 0; i < loopCount; i++)
{
sum += (s ?? "o").Length;
}
watch.Stop();
Console.WriteLine($"?? operator took {watch.ElapsedMilliseconds} ms");
sum = 0;
watch.Restart();
for (int i = 0; i < loopCount; i++)
{
sum += (s != null ? s : "o").Length;
}
watch.Stop();
Console.WriteLine($"null-check condition took {watch.ElapsedMilliseconds} ms");
}
Donc la réponse est oui, il y a une différence.
PS. StackOverflow devrait automatiquement avertir les messages mentionnant "performance" et "négligeable" dans la même phrase. Seule l'affiche originale peut savoir avec certitude si une unité de temps est négligeable.