web-dev-qa-db-fra.com

Comment savoir si un type est un type "simple"? c.-à-d. détient une seule valeur

typeof(string).IsPrimitive == false
typeof(int).IsPrimitive == true
typeof(MyClass).IsClass == true
typeof(string).IsClass == true
typeof(string).IsByRef == false
typeof(MyClass).IsByRef == true // correction: should be false (see comments below)

J'ai une méthode qui instancie une nouvelle instance de T et, s'il s'agit d'une classe "complexe", remplit ses propriétés à partir d'un ensemble de valeurs de données source.

(a) Si T est un type simple (par exemple une chaîne ou un entier ou toute autre chose similaire), une conversion rapide des données source en T doit être effectuée.

(b) Si T est une classe (mais pas quelque chose de simple comme une chaîne), alors j'utiliserai Activator.CreateInstance et ferai un peu de réflexion pour remplir les champs.

Existe-t-il un moyen rapide et simple de dire si je dois utiliser la méthode (a) ou la méthode (b)? Cette logique sera utilisée dans une méthode générique avec T comme argument de type.

57
Nathan Ridley

La chaîne est probablement un cas spécial.

Je pense que je ferais .....

bool IsSimple(Type type)
{
    return type.IsPrimitive 
      || type.Equals(typeof(string));
}

Modifier:

Parfois, vous devez couvrir d'autres cas, comme les énumérations et les décimales. Les énumérations sont un type spécial de type en C #. Les décimales sont des structures comme les autres. Le problème avec les structures est qu'elles peuvent être complexes, elles peuvent être des types définis par l'utilisateur, elles peuvent être juste un nombre. Vous n'avez donc aucune autre chance que de les connaître pour se différencier.

bool IsSimple(Type type)
{
  return type.IsPrimitive 
    || type.IsEnum
    || type.Equals(typeof(string))
    || type.Equals(typeof(decimal));
}

La gestion des équivalents annulables est également un peu délicate. Le nullable lui-même est une structure.

bool IsSimple(Type type)
{
  if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
  {
    // nullable type, check if the nested type is simple.
    return IsSimple(type.GetGenericArguments()[0]);
  }
  return type.IsPrimitive 
    || type.IsEnum
    || type.Equals(typeof(string))
    || type.Equals(typeof(decimal));
}

Tester:

Assert.IsTrue(IsSimple(typeof(string)));
Assert.IsTrue(IsSimple(typeof(int)));
Assert.IsTrue(IsSimple(typeof(decimal)));
Assert.IsTrue(IsSimple(typeof(float)));
Assert.IsTrue(IsSimple(typeof(StringComparison)));  // enum
Assert.IsTrue(IsSimple(typeof(int?)));
Assert.IsTrue(IsSimple(typeof(decimal?)));
Assert.IsTrue(IsSimple(typeof(StringComparison?)));
Assert.IsFalse(IsSimple(typeof(object)));
Assert.IsFalse(IsSimple(typeof(Point)));  // struct in System.Drawing
Assert.IsFalse(IsSimple(typeof(Point?)));
Assert.IsFalse(IsSimple(typeof(StringBuilder))); // reference type

Note à .NET Core

Comme DucoJ le souligne dans sa réponse , certaines des méthodes utilisées ne sont plus disponibles sur la classe Type dans le noyau .NET.

Code fixe (j'espère que cela fonctionne, je n'ai pas pu m'essayer. Sinon, veuillez commenter):

bool IsSimple(Type type)
{
  var typeInfo = type.GetTypeInfo();
  if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>))
  {
    // nullable type, check if the nested type is simple.
    return IsSimple(typeInfo.GetGenericArguments()[0]);
  }
  return typeInfo.IsPrimitive 
    || typeInfo.IsEnum
    || type.Equals(typeof(string))
    || type.Equals(typeof(decimal));
}
105

En plus de la réponse de Stefan Steinegger: dans .NET Core, les .IsPrimitive, etc. ne sont plus membres de Type, ils sont désormais membres de TypeInfo. Sa solution deviendra alors:

bool IsSimple(TypeInfo type)
{
    if (type.IsGenericType && type.GetGenericTypeDefinition() ==     typeof(Nullable<>))
    {
        // nullable type, check if the nested type is simple.
        return IsSimple((type.GetGenericArguments()[0]).GetTypeInfo());
    }
    return type.IsPrimitive
      || type.IsEnum
      || type.Equals(typeof(string))
      || type.Equals(typeof(decimal));
}
16
DucoJ

Il existe un type plus général que primitif, le ValueType englobe beaucoup plus que les primitifs, tels que les énumérations, les décimales et d'autres choses de ce type ValueType . Vous trouverez ci-dessous une fonction que j'ai écrite pour identifier les types complexes, qui peuvent répondre à vos besoins.

    public static bool IsComplex(Type typeIn)
    {
        if (typeIn.IsSubclassOf(typeof(System.ValueType)) || typeIn.Equals(typeof(string))) //|| typeIn.IsPrimitive
            return false;
        else
            return true;

    }
6
Mauser

Désolé de ressusciter un fil de discussion très ancien, mais comme il occupe toujours une place de choix dans les recherches sur le Web dans Google, vous souhaitez obtenir une solution plus directe et efficace ajoutée:

if(System.Type.GetTypeCode(typeof(int)) == TypeCode.Object) {
    // Do what you will...
}
3
Capt Nasty

Cela n'a peut-être pas d'importance, mais il semble que vous omettez quelques cas:

  1. Types complexes qui ont des conversions
  2. Types de valeurs qui n'ont pas de constructeur sans paramètre. Exemple ci-dessous:

Il y en a probablement plus, mais je pense que vous partitionnez l'espace problématique d'une manière trop restrictive.

 public class Person {
    private string _name;
    private int _age;
    public Person(string name, int age) {_name = name; _age = age;}
    // Remainder of value implementation
 }
1
John Saunders

La réponse de Mauser modifiée a ajouté un peu une méthode pour vérifier si une propriété est une collection.

public static class TypeExtensions
{
    public static bool IsComplex(this Type type)
    {
        return !type.IsValueType && type != typeof(string);
    }

    public static bool IsCollection(this Type type)
    {
        var collectionTypeName = typeof(ICollection<>).Name;
        return type.Name == collectionTypeName || type.GetInterface(collectionTypeName) != null;
    }
}

Ici, dans IsCollection (..), nous pouvons même conserver IEnumerable, mais la chaîne hérite également IEnumerable. donc si vous utilisez IEnumerable, ajoutez également une vérification de chaîne!

public static class TypeExtensions
    {

        public static bool IsComplex(this Type type)
        {
            return !type.IsValueType && type != typeof(string);
        }



        public static bool IsCustomComplex(this Type type)
        {
            var elementType = type.GetCustomElementType();
            return elementType != null && elementType.IsComplex();
        }

        public static Type GetCustomElementType(this Type type, object value)
        {
            return value != null 
                ? value.GetType().GetCustomElementType() 
                : type.GetCustomElementType();
        }

        public static Type GetCustomElementType(this Type type)
        {
            return type.IsCollection()
                ? type.IsArray
                    ? type.GetElementType()
                    : type.GetGenericArguments()[0]
                : type;
        }


        public static bool IsCustomComplex(this Type type, object value)
        {
            return value != null
                ? value.GetType().IsCustomComplex()
                : type.IsCustomComplex();
        }


        public static bool IsCollection(this Type type)
        {
            var collectionTypeName = typeof(IEnumerable<>).Name;
            return (type.Name == collectionTypeName || type.GetInterface(typeof(IEnumerable<>).Name) != null ||
                    type.IsArray) && type != typeof(string);
        }

        public static bool HasDefaultConstructor(this Type type)
        {
            return type.IsValueType || type.GetConstructor(Type.EmptyTypes) != null;
        }

    }
0
Shyam Kumar

Les cordes ne sont pas primitives, si je me souviens bien. même s'il existe un mot-clé, une chaîne est un objet. Votre appel à IsPrimitive vous indiquera avec précision si quelque chose est une primitive.

0
Annath