71 votes

Vérification du type générique

Existe-t-il un moyen d'imposer/limiter les types qui sont transmis aux primitives ? (bool, int, string, etc.)

Maintenant, je sais que vous pouvez limiter le paramètre de type générique à un type ou à une implémentation d'interface via la fonction clause. Cependant, cela ne convient pas aux primitives (AFAIK) car elles n'ont pas toutes un point commun (à l'exception des objet avant que quelqu'un ne le dise ! :P).

Donc, mes pensées actuelles sont de serrer les dents et de faire un gros commutateur et lancer un ArgumentException en cas d'échec.


EDIT 1 :

Juste pour clarifier :

La définition du code devrait être comme ceci :

public class MyClass<GenericType> ....

Et l'instanciation :

MyClass<bool> = new MyClass<bool>(); // Legal
MyClass<string> = new MyClass<string>(); // Legal
MyClass<DataSet> = new MyClass<DataSet>(); // Illegal
MyClass<RobsFunkyHat> = new MyClass<RobsFunkyHat>(); // Illegal (but looks awesome!)

EDIT 2

@Jon Limjap - Bon point, et quelque chose que je considérais déjà. Je suis sûr qu'il existe une méthode générique qui peut être utilisée pour déterminer si le type est de type valeur ou référence.

Cela pourrait être utile pour supprimer instantanément un grand nombre d'objets dont je ne veux pas m'occuper (mais il faut alors se préoccuper des structs utilisés tels que Taille ). Un problème intéressant non ? :)

C'est ici :

where T: struct

Tiré de MSDN .


Je suis curieux. Est-ce que cela pourrait être fait dans .NET 3.x en utilisant des méthodes d'extension ? Créer une interface, et implémenter l'interface dans les méthodes d'extension (ce qui serait probablement plus propre qu'un commutateur un peu gros). De plus, si vous avez besoin d'étendre ultérieurement à des types personnalisés légers, ils peuvent également mettre en œuvre la même interface, sans qu'il soit nécessaire de modifier le code de base.

Qu'est-ce que vous en pensez ?

La triste nouvelle, c'est que je travaille dans le Framework 2 ! !! :D


EDIT 3

C'était si simple, suite à Jon Limjaps Pointer .. Si simple que j'ai presque envie de pleurer, mais c'est génial car le code fonctionne comme un charme !

Voici donc ce que j'ai fait (vous allez rire !) :

Code ajouté à la classe générique

bool TypeValid()
{
    // Get the TypeCode from the Primitive Type
    TypeCode code = Type.GetTypeCode(typeof(PrimitiveDataType));

    // All of the TypeCode Enumeration refer Primitive Types
    // with the exception of Object and Empty (Null).
    // Since I am willing to allow Null Types (at this time)
    // all we need to check for is Object!
    switch (code)
    {
        case TypeCode.Object:
            return false;
        default:
            return true;
    }
}

Puis une petite méthode utilitaire pour vérifier le type et lancer une exception,

private void EnforcePrimitiveType()
{
    if (!TypeValid())
        throw new InvalidOperationException(
            "Unable to Instantiate SimpleMetadata based on the Generic Type of '" + typeof(PrimitiveDataType).Name + 
            "' - this Class is Designed to Work with Primitive Data Types Only.");
}

Il suffit alors d'appeler EnforcePrimitiveType() dans les constructeurs des classes. C'est fait ! :-)

Le seul inconvénient est qu'il ne lève une exception qu'au moment de l'exécution (évidemment) plutôt qu'au moment de la conception. Mais ce n'est pas un gros problème et peut être récupéré avec des utilitaires comme FxCop (que nous n'utilisons pas au travail).

Un grand merci à Jon Limjap sur ce coup-là !

0 votes

Vous pouvez également appeler la vérification dans votre constructeur statique, afin qu'elle ne soit appelée qu'une seule fois par type utilisé comme argument générique.

74voto

Lars Mæhlum Points 4569
public class Class1<GenericType> where GenericType : struct
{
}

Celui-ci semble faire l'affaire

40voto

Jon Limjap Points 46429

Les primitives semblent être spécifiées dans le TypeCode l'énumération :

Il existe peut-être un moyen de savoir si un objet contient l'attribut TypeCode enum sans avoir à le convertir en un objet spécifique ou à l'appeler GetType() ou typeof() ?

Mise à jour C'était juste sous mon nez. L'exemple de code montre ceci :

static void WriteObjectInfo(object testObject)
{
    TypeCode    typeCode = Type.GetTypeCode( testObject.GetType() );

    switch( typeCode )
    {
        case TypeCode.Boolean:
            Console.WriteLine("Boolean: {0}", testObject);
            break;

        case TypeCode.Double:
            Console.WriteLine("Double: {0}", testObject);
            break;

        default:
            Console.WriteLine("{0}: {1}", typeCode.ToString(), testObject);
            break;
        }
    }
}

C'est toujours un commutateur moche. Mais c'est un bon point de départ !

0 votes

Eh bien, je ne sais pas comment, mais un des principaux points de discorde entre les objets et les primitives est que la plupart des primitives sont en fait des structs, et non des classes. Je vais chercher un moyen de les déterminer par programme :)

0 votes

Bon point, et quelque chose que j'envisageais déjà Je suis sûr qu'il existe une méthode générique qui peut être utilisée pour déterminer si le type est de type valeur ou référence Cela pourrait être utile pour supprimer instantanément un grand nombre d'objets dont je ne veux pas m'occuper (mais il faut alors se préoccuper des structs qui sont utilisés tels que Taille ).. Un problème intéressant non ? :) EDIT : Ah oui, le voici : où T : struct Pris de MSDN .

0 votes

Je suis curieux Je me demande si cela pourrait être fait dans .NET 3.x en utilisant des méthodes d'extension Créer une interface Implémenter l'interface dans les méthodes d'extension (ce qui serait probablement plus propre qu'un commutateur un peu gros) De plus, si vous avez besoin d'étendre ultérieurement à des types personnalisés légers, ils peuvent également mettre en œuvre la même interface, sans avoir à modifier le code de base. Qu'est-ce que vous en pensez ? La triste nouvelle est que je travaille dans le cadre 2 !! :D EDIT : Je quitte le bureau dans 5 minutes, je le prendrai en rentrant à la maison ! :D

22voto

Keith Points 46288

A peu près ce que @Lars a déjà dit :

//Force T to be a value (primitive) type.
public class Class1<T> where T: struct

//Force T to be a reference type.
public class Class1<T> where T: class

//Force T to be a parameterless constructor.
public class Class1<T> where T: new()

Tous fonctionnent en .NET 2, 3 et 3.5.

4voto

Doug McClean Points 6355

Si vous pouvez tolérer l'utilisation de méthodes d'usine (au lieu des constructeurs de MyClass que vous avez demandés), vous pouvez toujours faire quelque chose comme ceci :

class MyClass<T>
{
  private readonly T _value;

  private MyClass(T value) { _value = value; }

  public static MyClass<int> FromInt32(int value) { return new MyClass<int>(value); }
  public static MyClass<string> FromString(string value) { return new MyClass<string>(value); }
  // etc for all the primitive types, or whatever other fixed set of types you are concerned about
}

Un problème ici est que vous devriez taper MyClass<AnyTypeItDoesntMatter>.FromInt32 ce qui est ennuyeux. Il n'y a pas de bonne façon de contourner ce problème si vous voulez maintenir le caractère privé du constructeur, mais voici quelques solutions de contournement :

  • Créer une classe abstraite MyClass . Faire MyClass<T> hériter de MyClass et le nicher dans MyClass . Déplacez les méthodes statiques vers MyClass . Cela permettra de régler tous les problèmes de visibilité, au prix de devoir accéder à MyClass<T> comme MyClass.MyClass<T> .
  • Utilisez MyClass<T> comme indiqué. Créez une classe statique MyClass qui appelle les méthodes statiques dans MyClass<T> en utilisant MyClass<AnyTypeItDoesntMatter> (en utilisant probablement le type approprié à chaque fois, juste pour rigoler).
  • (Plus facile, mais certainement bizarre) Faire un type abstrait MyClass qui hérite de MyClass<AnyTypeItDoesntMatter> . (Pour être plus concret, disons que MyClass<int> .) Étant donné que vous pouvez appeler les méthodes statiques définies dans une classe de base par le biais du nom d'une classe dérivée, vous pouvez maintenant utiliser la fonction MyClass.FromString .

Cela vous donne une vérification statique au détriment d'une écriture plus importante.

Si vous êtes satisfait de la vérification dynamique, j'utiliserais une variante de la solution TypeCode ci-dessus.

3voto

Matt Boehlig Points 402

@Rob, Enum vont passer à travers le TypeValid car il s'agit d'une fonction TypeCode est Integer . J'ai mis à jour la fonction pour qu'elle vérifie également les éléments suivants Enum .

Private Function TypeValid() As Boolean
    Dim g As Type = GetType(T)
    Dim code As TypeCode = Type.GetTypeCode(g)

    ' All of the TypeCode Enumeration refer Primitive Types
    ' with the exception of Object and Empty (Nothing).
    ' Note: must also catch Enum as its type is Integer.
    Select Case code
        Case TypeCode.Object
            Return False
        Case Else
            ' Enum's TypeCode is Integer, so check BaseType
            If g.BaseType Is GetType(System.Enum) Then
                Return False
            Else
                Return True
            End If
    End Select
End Function

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X