Pourquoi ne Enum.GetValues () renvoie les noms lors de l'utilisation de "var"?
Quelqu'un Peut-il expliquer cela?
Le texte d'Alt http://www.deviantsart.com/upload/g4knqc.png
using System;
namespace TestEnum2342394834
{
class Program
{
static void Main(string[] args)
{
//with "var"
foreach (var value in Enum.GetValues(typeof(ReportStatus)))
{
Console.WriteLine(value);
}
//with "int"
foreach (int value in Enum.GetValues(typeof(ReportStatus)))
{
Console.WriteLine(value);
}
}
}
public enum ReportStatus
{
Assigned = 1,
Analyzed = 2,
Written = 3,
Reviewed = 4,
Finished = 5
}
}
7 réponses
Enum.GetValues
est déclaré comme étant de retour Array
.
Le tableau qu'il renvoie contient les valeurs de ReportStatus
valeurs.
Par conséquent, le mot clé var
devient object
, et la variable value
contient des valeurs enum typées (encadrées).
L'appel Console.WriteLine
résout la surcharge qui prend un object
et appelle ToString()
sur l'objet, qui, pour les énumérations, renvoie le nom.
Lorsque vous itérez sur un int
, le compilateur convertit implicitement les valeurs en int
, et la variable value
tient valeurs normales (et non encadrées) int
.
Par conséquent, l'appel Console.WriteLine
résout la surcharge qui prend un int
et l'imprime.
Si vous changez int
en DateTime
(ou tout autre type), il compilera toujours, mais il lancera un InvalidCastException
à l'exécution.
Selon la documentation MSDN, la surcharge de Console.WriteLine
qui prend un object
en interne appelle ToString
sur son argument.
Lorsque vous faites foreach (var value in ...)
, votre variable value
est tapée object
(puisque, comme le souligne SLaks, Enum.GetValues
renvoie un Array
non typé) et donc votre Console.WriteLine
appelle object.ToString
qui est remplacé par System.Enum.ToString
. Et cette méthode renvoie le nom de l'enum.
Lorsque vous faites foreach (int value in ...)
, vous convertissez les valeurs enum en int
valeurs (au lieu de object
); donc Console.WriteLine
appelle System.Int32.ToString
.
FWIW, voici le code démonté de Enum.GetValues () (via réflecteur):
[ComVisible(true)]
public static Array GetValues(Type enumType)
{
if (enumType == null)
{
throw new ArgumentNullException("enumType");
}
if (!(enumType is RuntimeType))
{
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeType"), "enumType");
}
if (!enumType.IsEnum)
{
throw new ArgumentException(Environment.GetResourceString("Arg_MustBeEnum"), "enumType");
}
ulong[] values = GetHashEntry(enumType).values;
Array array = Array.CreateInstance(enumType, values.Length);
for (int i = 0; i < values.Length; i++)
{
object obj2 = ToObject(enumType, values[i]);
array.SetValue(obj2, i);
}
return array;
}
Ressemble à ce que tout le monde dit à propos du var
étant un object
et appelant object.ToString()
renvoyant le nom est correct...
Vous appelez implicitement ToString () sur chaque élément lorsque vous utilisez la Console.WriteLine.
Et quand vous dites que vous voulez un int (en utilisant le type explicite), il le convertira en un int - puis ToString ().
Le premier est la valeur Enum ToString () ' ed
EDIT: ajout d'un exemple de code qui explore beaucoup (peut-être tous?) des moyens possibles d'itérer sur le tableau.
Les types Enum sont considérés comme "dérivés" de int par défaut. Vous pouvez choisir de le dériver de l'un des autres types entiers si vous le souhaitez, tels que byte, short, long, etc.
Dans les deux cas, l'appel à Enum.GetValues
renvoie un tableau D'objets ReportStatus.
L'Utilisation du mot-clé var dans la première boucle indique au compilateur d'utiliser le type spécifié du tableau, qui est ReportStatus, pour déterminer le type de la variable de valeur. L'implémentation de ToString pour enums est de retourner le nom de l'entrée enum, pas la valeur entière qu'elle représente, c'est pourquoi les noms sont en sortie de la première boucle.
L'utilisation d'une variable int dans la deuxième boucle entraîne la conversion implicite des valeurs renvoyées par Enum.GetValues
de ReportStatus en int. Appeler ToString sur un int retournera, bien sûr, une chaîne représentant la valeur entière. L'implicite la conversion est ce qui cause la différence de comportement.
UPDATE: comme d'autres l'ont souligné, L'énumération.La fonction GetValues renvoie un objet typé en tant que Tableau et, par conséquent, il s'agit d'un énumérable de types D'Objets, pas de types ReportStatus.
Indépendamment, le résultat final est le même si itérer sur Array ou ReportStatus []:
class Program
{
enum ReportStatus
{
Assigned = 1,
Analyzed = 2,
Written = 3,
Reviewed = 4,
Finished = 5,
}
static void Main(string[] args)
{
WriteValues(Enum.GetValues(typeof(ReportStatus)));
ReportStatus[] values = new ReportStatus[] {
ReportStatus.Assigned,
ReportStatus.Analyzed,
ReportStatus.Written,
ReportStatus.Reviewed,
ReportStatus.Finished,
};
WriteValues(values);
}
static void WriteValues(Array values)
{
foreach (var value in values)
{
Console.WriteLine(value);
}
foreach (int value in values)
{
Console.WriteLine(value);
}
}
static void WriteValues(ReportStatus[] values)
{
foreach (var value in values)
{
Console.WriteLine(value);
}
foreach (int value in values)
{
Console.WriteLine(value);
}
}
}
Juste pour un peu de plaisir supplémentaire, j'ai ajouté du code ci-dessous démontrant plusieurs façons différentes d'itérer sur le tableau spécifié avec un foreach boucle, y compris les commentaires qui décrit en détail ce qui se passe dans chaque cas.
class Program
{
enum ReportStatus
{
Assigned = 1,
Analyzed = 2,
Written = 3,
Reviewed = 4,
Finished = 5,
}
static void Main(string[] args)
{
Array values = Enum.GetValues(typeof(ReportStatus));
Console.WriteLine("Type of array: {0}", values.GetType().FullName);
// Case 1: iterating over values as System.Array, loop variable is of type System.Object
// The foreach loop uses an IEnumerator obtained from System.Array.
// The IEnumerator's Current property uses the System.Array.GetValue method to retrieve the current value, which uses the TypedReference.InternalToObject function.
// The value variable is passed to Console.WriteLine(System.Object).
// Summary: 0 box operations, 0 unbox operations, 1 usage of TypedReference
Console.WriteLine("foreach (object value in values)");
foreach (object value in values)
{
Console.WriteLine(value);
}
// Case 2: iterating over values as System.Array, loop variable is of type ReportStatus
// The foreach loop uses an IEnumerator obtained from System.Array.
// The IEnumerator's Current property uses the System.Array.GetValue method to retrieve the current value, which uses the TypedReference.InternalToObject function.
// The current value is immediatly unboxed as ReportStatus to be assigned to the loop variable, value.
// The value variable is then boxed again so that it can be passed to Console.WriteLine(System.Object).
// Summary: 1 box operation, 1 unbox operation, 1 usage of TypedReference
Console.WriteLine("foreach (ReportStatus value in values)");
foreach (ReportStatus value in values)
{
Console.WriteLine(value);
}
// Case 3: iterating over values as System.Array, loop variable is of type System.Int32.
// The foreach loop uses an IEnumerator obtained from System.Array.
// The IEnumerator's Current property uses the System.Array.GetValue method to retrieve the current value, which uses the TypedReference.InternalToObject function.
// The current value is immediatly unboxed as System.Int32 to be assigned to the loop variable, value.
// The value variable is passed to Console.WriteLine(System.Int32).
// Summary: 0 box operations, 1 unbox operation, 1 usage of TypedReference
Console.WriteLine("foreach (int value in values)");
foreach (int value in values)
{
Console.WriteLine(value);
}
// Case 4: iterating over values as ReportStatus[], loop variable is of type System.Object.
// The foreach loop is compiled as a simple for loop; it does not use an enumerator.
// On each iteration, the current element of the array is assigned to the loop variable, value.
// At that time, the current ReportStatus value is boxed as System.Object.
// The value variable is passed to Console.WriteLine(System.Object).
// Summary: 1 box operation, 0 unbox operations
Console.WriteLine("foreach (object value in (ReportStatus[])values)");
foreach (object value in (ReportStatus[])values)
{
Console.WriteLine(value);
}
// Case 5: iterating over values as ReportStatus[], loop variable is of type ReportStatus.
// The foreach loop is compiled as a simple for loop; it does not use an enumerator.
// On each iteration, the current element of the array is assigned to the loop variable, value.
// The value variable is then boxed so that it can be passed to Console.WriteLine(System.Object).
// Summary: 1 box operation, 0 unbox operations
Console.WriteLine("foreach (ReportStatus value in (ReportStatus[])values)");
foreach (ReportStatus value in (ReportStatus[])values)
{
Console.WriteLine(value);
}
// Case 6: iterating over values as ReportStatus[], loop variable is of type System.Int32.
// The foreach loop is compiled as a simple for loop; it does not use an enumerator.
// On each iteration, the current element of the array is assigned to the loop variable, value.
// The value variable is passed to Console.WriteLine(System.Int32).
// Summary: 0 box operations, 0 unbox operations
Console.WriteLine("foreach (int value in (ReportStatus[])values)");
foreach (int value in (ReportStatus[])values)
{
Console.WriteLine(value);
}
// Case 7: The compiler evaluates var to System.Object. This is equivalent to case #1.
Console.WriteLine("foreach (var value in values)");
foreach (var value in values)
{
Console.WriteLine(value);
}
// Case 8: The compiler evaluates var to ReportStatus. This is equivalent to case #5.
Console.WriteLine("foreach (var value in (ReportStatus[])values)");
foreach (var value in (ReportStatus[])values)
{
Console.WriteLine(value);
}
}
}
-- mis à jour mes commentaires dans l'exemple ci-dessus; en vérifiant à nouveau, j'ai vu que le système.Tableau.La méthode GetValue utilise en fait la classe TypedReference afin d'extraire un élément du tableau et de le renvoyer en tant que système.Objet. J'avais initialement écrit qu'il y avait une opération de boxe qui s'y passait, mais ce n'est techniquement pas le cas. Je ne sais pas quelle est la comparaison d'une opération de boîte par rapport à un appel à TypedReference.InternalToObject; je suppose que cela dépend de L'implémentation CLR. Quoi qu'il en soit, je crois que les détails sont plus ou moins corrects maintenant.
Un type d'énumération est distinct d'un nombre entier. Dans votre échantillon, var
n'évalue pas int, il évalue le type d'énumération. Vous obtiendriez la même sortie si vous aviez utilisé le type d'énumération lui-même.
Les types D'énumération affichent le nom lorsqu'ils sont imprimés, pas leur valeur.
Le var value
est en fait une valeur enum (de type ReportStatus), donc vous voyez le comportement standard de enumValue.ToString () - c'est le nom.
Modifier:
Lorsque vous faites un Console.WriteLine(value.GetType())
, vous verrez que c'est vraiment un 'ReportStatus', bien qu'il soit encadré dans une plaine Object
.