2009-08-06 7 views
3

Je regardais un exemple, et il a vu que je le codeIntPtr jeté contre nouvelle

return new IntPtr(handle); 

Après farfouillé notre code, je trouve que nous avons déjà utilisé un modèle similaire, mais dans notre code nous avions presque la même chose:

return (IntPtr)handle; 

Y at-il une différence entre ces deux prises? Est-ce que le second sera "meilleur" d'une quelconque façon, puisqu'il n'alloue pas de nouvelle mémoire, ou est-ce que le cast cache juste le même constructeur en dessous?

Répondre

8

Dans vos exemples, je suppose que handle est une valeur entière? IntPtr déclare une conversion explicite de Int32 (int) et Int64 (long) qui appelle simplement le même constructeur:

public static explicit operator IntPtr(int value) 
{ 
    return new IntPtr(value); 
} 

Donc, il n'y a effectivement pas de différence autre que les préoccupations possibles de lisibilité.

2

Étant donné que IntPtr est un type de valeur, l'utilisation de new n'alloue aucune mémoire.

Techniquement, les appels se compilent encore à différents IL - on appelle effectivement le constructeur, un autre appelle l'opérateur de conversion explicite. Je ne suis pas sûr s'il y a une différence réelle entre ces deux après une passe JIT, cependant - probablement pas (bien que je doute que vous remarquiez de toute façon dans la pratique, ce qui est une femtooptimization).

Dans tous les cas, la distribution est plus idiomatique que l'utilisation d'un constructeur, donc je suggère d'y aller pour cette seule raison.

+0

Le fait que IntPtr soit un type de valeur ne signifie pas que new n'alloue pas de mémoire. En fait, l'utilisation de new devrait être préférée pour s'assurer que la structure est correctement initialisée. –

+0

Je pense que ce que Pavel voulait dire, c'est qu'il n'utilise pas de mémoire qui n'aurait pas été utilisée autrement. Évidemment, copier un type de valeur signifiera que plus de mémoire sera utilisée :) – Thorarin

+0

Il n'y a absolument aucun avantage à utiliser (ou ne pas utiliser) 'new' pour s'assurer que la structure est correctement initialisée. Pour commencer, vous ne pouvez pas avoir une "structure mal initialisée" en premier lieu, puisque le compilateur appliquera l'initialisation pour les locals, et par défaut initialisera tout le reste. –

5

Reflector dit que le casting appelle le constructeur sous le capot de toute façon:

[Serializable, StructLayout(LayoutKind.Sequential), ComVisible(true)] 
public struct IntPtr : ISerializable 
{ 
    ... 

    [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)] 
    public static explicit operator IntPtr(int value) 
    { 
     return new IntPtr(value); 
    } 

} 
2

Donc, ce fil est tout le monde parle et pas de chiffres permet donc des mesures de discussion. J'ai mesuré ces métriques en calculant le temps moyen de chaque méthode sur 10 tests avec 10 millions d'itérations chacun en mode Debug puis Release (non optimisé puis optimisé)):

(Debug) coulée Méthode: ~ 32 ms Méthode d'attribution: ~ 26 ms

(sortie) coulée Méthode: ~ 20 ms Méthode de répartition: ~ 22 ms

Qu'est-ce que Il est également intéressant de comparer ces métriques à un code similaire avec C++ géré en utilisant seulement gcnew et les résultats sont très différents.

Même configuration à nouveau. Sauf en comparant la méthode de coulée: "IntPtr^ptr = (IntPtr) i;" vs la méthode d'allocation: "IntPtr^ptr = (IntPtr) i;".

(Mise au point) casting Méthode: ~ 91ms Allocation Méthode: ~ 127ms

(Release) casting Méthode: ~ 22ms Méthode d'allocation: ~ 124ms

Maintenant, si vous gratter la tête en disant: Eh bien, pourquoi C# est-il tellement plus rapide que le C++ géré et la réponse est que non. Le moyen le plus efficace d'utiliser IntPtr est le type de valeur et non une référence à un type de valeur. Par exemple comme si "IntPtr ptr = (IntPtr) i;". Cela vous donnerait ~ 24ms (Debug more) ou (~ 22 Release mode). Voyez comment il a été optimisé au-dessus par le compilateur pour obtenir le 22ms plutôt que le 90ms. Conclusion en C#, sauf si vous cherchez un code vraiment très serré, cela n'a pas d'importance. Je pense qu'avec mon code dans Release, c'était en fait en optimisant le casting, car commenter le casting donnait le même ~ 22ms. Mais pour la plupart compilateur a votre dos sur celui-ci en C# bien au moins VS 2010 fait. Cependant, dans Managed C++/CLI, si vous observez du code avec des contraintes de performances minimales, faites attention. Le compilateur n'optimisera pas automatiquement les allocations gcnew à l'approche casting et il est presque 6 fois plus rapide ... J'ai en fait rencontré ce problème particulier en C++/CLI qui est ce qui m'a amené à poster sur ce thread quand il s'agissait d'audio en temps réel En traitement. Mon code (C#): (Mon code C++ géré était très similaire sauf que je devais écrire Average() moi-même et utiliser la console pour sortir plutôt que des boîtes de message).

static void Main() 
    { 
     List<int> castTimings = new List<int>(); 
     List<int> allocTimings = new List<int>(); 

     for (int i = 0; i < TEST_RUNS; ++i) 
     { 
      castTimings.Add(RunCastMethod().Milliseconds); 
      allocTimings.Add(RunAllocationMethod().Milliseconds); 
     } 

     MessageBox.Show(string.Format("Casting Method took: {0}ms", castTimings.Average())); 
     MessageBox.Show(string.Format("Allocation Method took: {0}ms", allocTimings.Average())); 
    } 

    private static TimeSpan RunAllocationMethod() { 
     DateTime start = DateTime.Now; 

     for (int i = 0; i < TEST_ITERATIONS; ++i) 
     { 
      IntPtr ptr = new IntPtr(i); 
     } 

     return (DateTime.Now - start); 
    } 

    private static TimeSpan RunCastMethod() 
    { 
     DateTime start = DateTime.Now; 

     for (int i = 0; i < TEST_ITERATIONS; ++i) 
     { 
      IntPtr ptr = (IntPtr) i; 
     } 

     return (DateTime.Now - start); 
    } 
+0

Merci pour l'effort mede –