Question

Ma question concerne c# et comment accéder aux membres statiques...Eh bien, je ne sais pas vraiment comment l'expliquer (ce qui est mauvais pour une question, n'est-ce pas ?). Je vais juste vous donner un exemple de code :

Class test<T>{
     int method1(Obj Parameter1){
         //in here I want to do something which I would explain as
         T.TryParse(Parameter1);

         //my problem is that it does not work ... I get an error.
         //just to explain: if I declare test<int> (with type Integer)
         //I want my sample code to call int.TryParse(). If it were String
         //it should have been String.TryParse()
     }
}

Alors merci les gars pour vos réponses (au fait, la question est :comment puis-je résoudre ce problème sans obtenir d'erreur).C’est probablement une question assez simple pour vous !

Merci, Niklas


Modifier:Merci à tous pour vos réponses !

Même si je pense que le slogan try - catch est le plus élégant, je sais, d'après mon expérience avec VB, que cela peut vraiment être décevant.Je l'ai utilisé une fois et il a fallu environ 30 minutes pour exécuter un programme, qui n'a ensuite pris que 2 minutes pour calculer simplement parce que j'avais évité d'essayer - attraper.

C'est pourquoi j'ai choisi l'instruction swich comme la meilleure réponse.Cela rend le code plus compliqué mais d'un autre côté j'imagine qu'il est relativement rapide et relativement facile à lire.(Même si je pense toujours qu'il devrait y avoir une manière plus élégante...peut-être dans la prochaine langue que j'apprendrai :P )


Mais si vous avez d'autres suggestions, j'attends toujours (et je suis prêt à participer)

Était-ce utile?

La solution

Une façon de plus de le faire, cette fois avec de la réflexion dans le mix :

static class Parser
{
    public static bool TryParse<TType>( string str, out TType x )
    {
        // Get the type on that TryParse shall be called
        Type objType = typeof( TType );

        // Enumerate the methods of TType
        foreach( MethodInfo mi in objType.GetMethods() )
        {
            if( mi.Name == "TryParse" )
            {
                // We found a TryParse method, check for the 2-parameter-signature
                ParameterInfo[] pi = mi.GetParameters();
                if( pi.Length == 2 ) // Find TryParse( String, TType )
                {
                    // Build a parameter list for the call
                    object[] paramList = new object[2] { str, default( TType ) };

                    // Invoke the static method
                    object ret = objType.InvokeMember( "TryParse", BindingFlags.InvokeMethod, null, null, paramList );

                    // Get the output value from the parameter list
                    x = (TType)paramList[1];
                    return (bool)ret;
                }
            }
        }

        // Maybe we should throw an exception here, because we were unable to find the TryParse
        // method; this is not just a unable-to-parse error.

        x = default( TType );
        return false;
    }
}

La prochaine étape consisterait à essayer de mettre en œuvre

public static TRet CallStaticMethod<TRet>( object obj, string methodName, params object[] args );

Avec correspondance complète du type de paramètre, etc.

Autres conseils

Le problème est que TryParse n’est défini nulle part sur une interface ou une classe de base, vous ne pouvez donc pas supposer que le type transmis à votre classe aura cette fonction.À moins que vous ne puissiez contraindre T d'une manière ou d'une autre, vous rencontrerez souvent ce problème.

Contraintes sur les paramètres de type

Réponse courte, vous ne pouvez pas.

Réponse longue, vous pouvez tricher :

public class Example
{
    internal static class Support
    {
        private delegate bool GenericParser<T>(string s, out T o);
        private static Dictionary<Type, object> parsers =
            MakeStandardParsers();
        private static Dictionary<Type, object> MakeStandardParsers()
        {
            Dictionary<Type, object> d = new Dictionary<Type, object>();
            // You need to add an entry for every type you want to cope with.
            d[typeof(int)] = new GenericParser<int>(int.TryParse);
            d[typeof(long)] = new GenericParser<long>(long.TryParse);
            d[typeof(float)] = new GenericParser<float>(float.TryParse);
            return d;
        }
        public static bool TryParse<T>(string s, out T result)
        {
            return ((GenericParser<T>)parsers[typeof(T)])(s, out result);
        }
    }
    public class Test<T>
    {
        public static T method1(string s)
        {
            T value;
            bool success = Support.TryParse(s, out value);
            return value;
        }
    }
    public static void Main()
    {
        Console.WriteLine(Test<int>.method1("23"));
        Console.WriteLine(Test<float>.method1("23.4"));
        Console.WriteLine(Test<long>.method1("99999999999999"));
        Console.ReadLine();
    }
}

J'ai créé un dictionnaire statique contenant un délégué pour la méthode TryParse de chaque type que je pourrais vouloir utiliser.J'ai ensuite écrit une méthode générique pour rechercher le dictionnaire et transmettre l'appel au délégué approprié.Étant donné que chaque délégué a un type différent, je les stocke simplement en tant que références d'objet et je les renvoie au type générique approprié lorsque je les récupère.Notez que pour un exemple simple, j'ai omis la vérification des erreurs, par exemple pour vérifier si nous avons une entrée dans le dictionnaire pour le type donné.

Pour accéder à un membre d'une classe ou d'une interface spécifique, vous devez utiliser le mot-clé Where et spécifier l'interface ou la classe de base qui possède la méthode.

Dans le cas ci-dessus, TryParse ne provient pas d'une interface ou d'une classe de base, donc ce que vous essayez de faire ci-dessus n'est pas possible.Il est préférable d'utiliser simplement Convert.ChangeType et une instruction try/catch.

class test<T>
{
    T Method(object P)
    {
       try {
           return (T)Convert.ChangeType(P, typeof(T));
       } catch(Exception e) {
           return null;
       }
    }
}

Voulez-vous faire quelque chose comme ceci :

Class test<T>
{
     T method1(object Parameter1){

         if( Parameter1 is T ) 
         {
              T value = (T) Parameter1;
             //do something with value
             return value;
         }
         else
         {
             //Parameter1 is not a T
             return default(T); //or throw exception
         }
     }
}

Malheureusement, vous ne pouvez pas vérifier le modèle TryParse car il est statique, ce qui signifie malheureusement qu'il n'est pas particulièrement adapté aux génériques.

La seule façon de faire exactement ce que vous recherchez serait d'utiliser la réflexion pour vérifier si la méthode existe pour T.

Une autre option consiste à garantir que l'objet que vous envoyez est un objet convertible en limitant le type à IConvertible (tous les types primitifs implémentent IConvertible).Cela vous permettrait de convertir votre paramètre en type donné de manière très flexible.

Class test<T>
{
    int method1(IConvertible Parameter1){

        IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T));

        T temp = Parameter1.ToType(typeof(T), provider);
    }
}

Vous pouvez également faire une variante à ce sujet en utilisant un type « objet » à la place, comme vous l'aviez fait à l'origine.

Class test<T>
{
    int method1(object Parameter1){

        if(Parameter1 is IConvertible) {

            IFormatProvider provider = System.Globalization.CultureInfo.CurrentCulture.GetFormat(typeof(T));

            T temp = Parameter1.ToType(typeof(T), provider);

        } else {
           // Do something else
        }
    }
}

Ok les gars:Merci pour tous les poissons.Maintenant avec vos réponses et mes recherches (notamment l'article sur limiter les types génériques aux primitives) Je vais vous présenter ma solution.

Class a<T>{
    private void checkWetherTypeIsOK()
    {
        if (T is int || T is float //|| ... any other types you want to be allowed){
            return true;
        }
        else {
            throw new exception();
        }
    }
    public static a(){
        ccheckWetherTypeIsOK();
    }
}

Ce n'est pas vraiment une solution, mais dans certains scénarios, cela pourrait être une bonne alternative :Nous pouvons passer un délégué supplémentaire à la méthode générique.

Pour clarifier ce que je veux dire, utilisons un exemple.Disons que nous avons une méthode d'usine générique, qui devrait créer une instance de T, et que nous voulons ensuite qu'elle appelle une autre méthode, pour une notification ou une initialisation supplémentaire.

Considérons la classe simple suivante :

public class Example
{
    // ...

    public static void PostInitCallback(Example example)
    {
        // Do something with the object...
    }
}

Et la méthode statique suivante :

public static T CreateAndInit<T>() where T : new()
{
    var t = new T();
    // Some initialization code...
    return t;
}

Il faudrait donc maintenant faire :

var example = CreateAndInit<Example>();
Example.PostInitCallback(example);

Cependant, nous pourrions changer notre méthode pour prendre un délégué supplémentaire :

public delegate void PostInitCallback<T>(T t);
public static T CreateAndInit<T>(PostInitCallback<T> callback) where T : new()
{
    var t = new T();
    // Some initialization code...
    callback(t);
    return t;
}

Et maintenant, nous pouvons modifier l'appel en :

var example = CreateAndInit<Example>(Example.PostInitCallback);

Évidemment, cela n’est utile que dans des scénarios très spécifiques.Mais c'est la solution la plus propre dans le sens où nous obtenons une sécurité au moment de la compilation, aucun "piratage" n'est impliqué et le code est extrêmement simple.

Vous ne pouvez probablement pas le faire.

Tout d'abord, si cela était possible, vous auriez besoin d'une limite plus stricte sur T afin que le vérificateur de type puisse être sûr que toutes les substitutions possibles pour T avaient réellement une méthode statique appelée TryParse.

Vous voudrez peut-être lire mon article précédent sur limiter les types génériques aux primitives.Cela peut vous donner quelques indications pour limiter le type pouvant être transmis au générique (puisque TypeAnalyser n'est évidemment disponible que pour un nombre défini de primitives ( chaîne.TryParse étant évidemment l'exception, ce qui n'a aucun sens).

Une fois que vous maîtrisez mieux le type, vous pouvez ensuite essayer de l'analyser.Vous aurez peut-être besoin d'un commutateur un peu laid (pour appeler le bon EssayezParse ) mais je pense que vous pouvez obtenir la fonctionnalité souhaitée.

Si vous avez besoin que je vous explique davantage l'un des points ci-dessus, n'hésitez pas à demander :)

Meilleur code :restreindre T à ValueType de cette façon :

class test1<T> where T: struct

Un « struct » signifie ici un type valeur.String est une classe, pas un type valeur.int, float, Enums sont tous des types valeur.

d'ailleurs, le compilateur n'accepte pas d'appeler des méthodes statiques ou d'accéder aux membres statiques sur les "paramètres de type" comme dans l'exemple suivant qui ne compilera pas :(

class MyStatic { public static int MyValue=0; }
class Test<T> where T: MyStatic
{
    public void TheTest() { T.MyValue++; }
}

=> Erreur 1 'T' est un 'paramètre de type', qui n'est pas valide dans le contexte donné

SL.

Ce n’est pas ainsi que fonctionne la statique.Vous devez considérer la statique comme une sorte de classe Globale, même si elle est répartie sur tout un tas de types.Ma recommandation est d'en faire une propriété à l'intérieur de l'instance T qui peut accéder à la méthode statique nécessaire.

De plus, T est une instance réelle de quelque chose et, comme toute autre instance, vous ne pouvez pas accéder aux statistiques de ce type, via la valeur instanciée.Voici un exemple de ce qu'il faut faire :

class a {
    static StaticMethod1 ()
    virtual Method1 ()
}

class b : a {
    override Method1 () return StaticMethod1()
}

class c : a {
    override Method1 () return "XYZ"
}

class generic<T> 
    where T : a {
    void DoSomething () T.Method1()
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top