Domanda

La mia domanda riguarda C# e come accedere ai membri statici ...Beh, non so davvero come spiegarlo (che tipo di domanda è negativa, non è vero?). Ti darò solo del codice di esempio:

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()
     }
}

Quindi grazie ragazzi per le vostre risposte (a proposito, la domanda è:come potrei risolvere questo problema senza ricevere un errore).Questa probabilmente è una domanda abbastanza facile per te!

Grazie, Niklas


Modificare:Grazie a tutti per le vostre risposte!

Anche se penso che la frase try-catch sia la più elegante, so dalla mia esperienza con vb che può davvero essere una seccatura.L'ho usato una volta e ci sono voluti circa 30 minuti per eseguire un programma, che in seguito ha richiesto solo 2 minuti per essere calcolato solo perché ho evitato try-catch.

Questo è il motivo per cui ho scelto l'istruzione switch come risposta migliore.Rende il codice più complicato ma d'altra parte immagino che sia relativamente veloce e relativamente facile da leggere.(Anche se penso ancora che ci dovrebbe essere un modo più elegante...forse nella prossima lingua che imparerò :P)


Tuttavia, se hai qualche altro suggerimento, sto ancora aspettando (e sono disposto a partecipare)

È stato utile?

Soluzione

Un altro modo per farlo, questa volta qualche riflessione nel 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;
    }
}

Il prossimo passo sarebbe provare a implementare

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

Con la corrispondenza completa del tipo di parametro, ecc.

Altri suggerimenti

Il problema è che TryParse non è definito su un'interfaccia o su una classe base da nessuna parte, quindi non puoi presupporre che il tipo passato alla tua classe avrà quella funzione.A meno che tu non riesca a limitare T in qualche modo, ti imbatterai spesso in questo.

Vincoli sui parametri di tipo

Risposta breve, non puoi.

Risposta lunga, puoi imbrogliare:

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();
    }
}

Ho creato un dizionario statico contenente un delegato per il metodo TryParse di ogni tipo che potrei voler utilizzare.Ho quindi scritto un metodo generico per cercare nel dizionario e passare la chiamata al delegato appropriato.Poiché ogni delegato ha un tipo diverso, li memorizzo semplicemente come riferimenti a oggetti e li ritrasformo nel tipo generico appropriato quando li recupero.Si noti che per motivi di semplice esempio ho omesso il controllo degli errori, ad esempio per verificare se nel dizionario è presente una voce per il tipo specificato.

Per accedere a un membro di una classe o interfaccia specifica è necessario utilizzare la parola chiave Where e specificare l'interfaccia o la classe base che contiene il metodo.

Nell'esempio sopra TryParse non proviene da un'interfaccia o da una classe base, quindi ciò che stai tentando di fare sopra non è possibile.È meglio usare semplicemente Convert.ChangeType e un'istruzione try/catch.

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

Intendi fare qualcosa del genere:

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
         }
     }
}

Sfortunatamente non puoi verificare il pattern TryParse poiché è statico, il che sfortunatamente significa che non è particolarmente adatto ai generici.

L'unico modo per fare esattamente quello che stai cercando sarebbe usare la riflessione per verificare se il metodo esiste per T.

Un'altra opzione è garantire che l'oggetto inviato sia un oggetto convertibile limitando il tipo a IConvertible (tutti i tipi primitivi implementano IConvertible).Ciò ti consentirebbe di convertire il tuo parametro nel tipo specificato in modo molto flessibile.

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

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

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

Potresti anche fare una variazione su questo usando invece un tipo 'oggetto' come avevi originariamente.

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 ragazzi:Grazie per tutto il pesce.Ora, con le tue risposte e la mia ricerca (in particolare l'articolo su limitando i tipi generici ai primitivi) Ti presenterò la mia soluzione.

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();
    }
}

Questa non è realmente una soluzione, ma in alcuni scenari potrebbe essere una buona alternativa:Possiamo passare un delegato aggiuntivo al metodo generico.

Per chiarire cosa intendo, utilizziamo un esempio.Diciamo che abbiamo un metodo factory generico, che dovrebbe creare un'istanza di T, e vogliamo che chiami poi un altro metodo, per la notifica o l'inizializzazione aggiuntiva.

Consideriamo la seguente semplice classe:

public class Example
{
    // ...

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

E il seguente metodo statico:

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

Quindi in questo momento dovremmo fare:

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

Tuttavia, potremmo cambiare il nostro metodo per prendere un delegato aggiuntivo:

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;
}

E ora possiamo cambiare la chiamata in:

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

Ovviamente questo è utile solo in scenari molto specifici.Ma questa è la soluzione più pulita, nel senso che otteniamo sicurezza in fase di compilazione, non è coinvolto alcun "hacking" e il codice è semplicissimo.

Probabilmente non puoi farlo.

Prima di tutto, se fosse possibile, avresti bisogno di un limite più stretto su T in modo che il controllore dei tipi possa essere sicuro che tutte le possibili sostituzioni per T abbiano effettivamente un metodo statico chiamato TryParse.

Potresti voler leggere il mio post precedente su limitando i tipi generici ai primitivi.Questo potrebbe darti alcune indicazioni per limitare il tipo che può essere passato al generico (since DigitareParse è ovviamente disponibile solo per un determinato numero di primitive ( stringa.TryParse ovviamente essendo l'eccezione, il che non ha senso).

Una volta che hai più controllo sul tipo, puoi provare ad analizzarlo.Potrebbe essere necessario un brutto interruttore lì dentro (per chiamare il file corretto ProvaParse ) ma penso che tu possa ottenere la funzionalità desiderata.

Se hai bisogno che ti spieghi ulteriormente quanto sopra, chiedi pure :)

Miglior codice:limitare T a ValueType in questo modo:

class test1<T> where T: struct

Una "struttura" qui indica un tipo di valore.String è una classe, non un tipo di valore.int, float, Enum sono tutti tipi di valore.

tra l'altro il compilatore non accetta di chiamare metodi statici o accedere a membri statici su "parametri di tipo" come nell'esempio seguente che non verrà compilato :(

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

=> L'errore 1 'T' è un 'parametro di tipo', che non è valido nel contesto specificato

SL.

Non è così che funziona la statica.Devi pensare alle statistiche come a una sorta di classe globale, anche se sono distribuite su tutta una serie di tipi.Il mio consiglio è di renderlo una proprietà all'interno dell'istanza T che possa accedere al metodo statico necessario.

Inoltre T è un'istanza effettiva di qualcosa e, proprio come qualsiasi altra istanza, non è possibile accedere alle statistiche per quel tipo, tramite il valore istanziato.Ecco un esempio di cosa fare:

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()
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top