Frage

Meine Frage betrifft C# und den Zugriff auf statische Mitglieder ...Nun, ich weiß nicht wirklich, wie ich es erklären soll (was ist für eine Frage schlecht, nicht wahr?). Ich gebe Ihnen nur einen Beispielcode:

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

Vielen Dank also für eure Antworten (Übrigens lautet die Frage:Wie würde ich dieses Problem lösen, ohne eine Fehlermeldung zu erhalten).Das ist wahrscheinlich eine ganz einfache Frage für Sie!

Danke, Niklas


Bearbeiten:Vielen Dank an alle für eure Antworten!

Obwohl ich denke, dass der Try-Slogan am elegantesten ist, weiß ich aus meiner Erfahrung mit vb, dass es wirklich eine Enttäuschung sein kann.Ich habe es einmal verwendet und es dauerte ungefähr 30 Minuten, ein Programm auszuführen, dessen Berechnung später nur noch 2 Minuten dauerte, nur weil ich Try-Catch vermieden habe.

Aus diesem Grund habe ich die Swich-Aussage als beste Antwort gewählt.Dadurch wird der Code komplizierter, andererseits stelle ich mir vor, dass er relativ schnell und relativ einfach zu lesen ist.(Obwohl ich immer noch denke, dass es einen eleganteren Weg geben sollte ...vielleicht in der nächsten Sprache, die ich lerne :P)


Wenn Sie jedoch einen anderen Vorschlag haben, warte ich immer noch (und bin bereit, mitzumachen).

War es hilfreich?

Lösung

Eine weitere Möglichkeit, dies zu tun, diesmal etwas Reflexion in der Mischung:

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

Der nächste Schritt wäre der Versuch der Umsetzung

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

Mit vollständiger Parametertypanpassung usw.

Andere Tipps

Das Problem besteht darin, dass TryParse nirgendwo auf einer Schnittstelle oder Basisklasse definiert ist, sodass Sie nicht davon ausgehen können, dass der an Ihre Klasse übergebene Typ diese Funktion hat.Sofern Sie T nicht auf irgendeine Weise einschränken können, werden Sie häufig darauf stoßen.

Einschränkungen für Typparameter

Kurze Antwort: Das geht nicht.

Lange Antwort, Sie können schummeln:

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

Ich habe ein statisches Wörterbuch erstellt, das einen Delegaten für die TryParse-Methode jedes Typs enthält, den ich verwenden möchte.Anschließend habe ich eine generische Methode geschrieben, um das Wörterbuch nachzuschlagen und den Aufruf an den entsprechenden Delegaten weiterzuleiten.Da jeder Delegat einen anderen Typ hat, speichere ich sie einfach als Objektverweise und setze sie beim Abrufen wieder in den entsprechenden generischen Typ um.Beachten Sie, dass ich zur Veranschaulichung eines einfachen Beispiels auf die Fehlerprüfung verzichtet habe, z. B. um zu überprüfen, ob für den angegebenen Typ ein Eintrag im Wörterbuch vorhanden ist.

Um auf ein Mitglied einer bestimmten Klasse oder Schnittstelle zuzugreifen, müssen Sie das Schlüsselwort Where verwenden und die Schnittstelle oder Basisklasse angeben, die über die Methode verfügt.

Im obigen Fall stammt TryParse nicht von einer Schnittstelle oder Basisklasse, daher ist das, was Sie oben versuchen, nicht möglich.Am besten verwenden Sie einfach Convert.ChangeType und eine try/catch-Anweisung.

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

Wollen Sie so etwas tun:

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

Leider können Sie nicht nach dem TryParse-Muster suchen, da es statisch ist – was leider bedeutet, dass es nicht besonders gut für Generika geeignet ist.

Die einzige Möglichkeit, genau das zu tun, was Sie suchen, besteht darin, mithilfe der Reflexion zu prüfen, ob die Methode für T existiert.

Eine andere Möglichkeit besteht darin, sicherzustellen, dass das von Ihnen gesendete Objekt ein konvertierbares Objekt ist, indem Sie den Typ auf IConvertible beschränken (alle primitiven Typen implementieren IConvertible).Dies würde es Ihnen ermöglichen, Ihren Parameter sehr flexibel in den angegebenen Typ zu konvertieren.

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

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

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

Sie können dies auch variieren, indem Sie statt wie ursprünglich einen „Objekt“-Typ verwenden.

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

Okay Leute:Danke für all die Fische.Nun mit Ihren Antworten und meiner Recherche (insbesondere dem Artikel über Beschränkung generischer Typen auf Grundelemente) Ich präsentiere Ihnen meine Lösung.

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

Dies ist keine wirkliche Lösung, könnte aber in bestimmten Szenarien eine gute Alternative sein:Wir können der generischen Methode einen zusätzlichen Delegaten übergeben.

Um zu verdeutlichen, was ich meine, verwenden wir ein Beispiel.Nehmen wir an, wir haben eine generische Factory-Methode, die eine Instanz von T erstellen soll, und wir möchten, dass sie dann eine andere Methode zur Benachrichtigung oder zusätzlichen Initialisierung aufruft.

Betrachten Sie die folgende einfache Klasse:

public class Example
{
    // ...

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

Und die folgende statische Methode:

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

Im Moment müssten wir also Folgendes tun:

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

Wir könnten jedoch unsere Methode ändern, um einen zusätzlichen Delegaten zu nehmen:

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

Und jetzt können wir den Aufruf ändern in:

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

Offensichtlich ist dies nur in ganz bestimmten Szenarien sinnvoll.Dies ist jedoch die sauberste Lösung in dem Sinne, dass wir Sicherheit bei der Kompilierung erhalten, kein „Hacking“ erforderlich ist und der Code absolut einfach ist.

Du schaffst es wahrscheinlich nicht.

Wenn es möglich sein sollte, benötigen Sie zunächst eine engere Grenze für T, damit der Typprüfer sicher sein kann, dass alle möglichen Ersetzungen für T tatsächlich eine statische Methode namens TryParse haben.

Vielleicht möchten Sie meinen vorherigen Beitrag lesen Beschränkung generischer Typen auf Grundelemente.Dies kann Ihnen einige Hinweise zur Einschränkung des Typs geben, der an das Generikum übergeben werden kann (da TypeParse ist offensichtlich nur für eine bestimmte Anzahl von Grundelementen verfügbar ( string.TryParse offensichtlich die Ausnahme, die keinen Sinn ergibt).

Sobald Sie den Typ besser im Griff haben, können Sie versuchen, ihn zu analysieren.Möglicherweise benötigen Sie dort einen etwas hässlichen Schalter (um das Richtige aufzurufen). TryParse ), aber ich denke, Sie können die gewünschte Funktionalität erreichen.

Wenn Sie eine weitere Erläuterung der oben genannten Punkte benötigen, fragen Sie mich bitte :)

Bester Code:beschränken Sie T auf ValueType auf diese Weise:

class test1<T> where T: struct

Eine „Struktur“ bedeutet hier einen Werttyp.String ist eine Klasse, kein Werttyp.int, float und Enums sind alles Werttypen.

Übrigens akzeptiert der Compiler nicht, statische Methoden aufzurufen oder auf statische Member für „Typparameter“ zuzugreifen, wie im folgenden Beispiel, das nicht kompiliert werden kann :(

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

=> Fehler 1 „T“ ist ein „Typparameter“, der im angegebenen Kontext nicht gültig ist

SL.

So funktioniert Statik nicht.Sie müssen sich die Statik als eine Art globale Klasse vorstellen, auch wenn sie über eine ganze Reihe von Typen verteilt ist.Meine Empfehlung ist, es zu einer Eigenschaft innerhalb der T-Instanz zu machen, die auf die erforderliche statische Methode zugreifen kann.

Außerdem ist T eine tatsächliche Instanz von etwas, und wie bei jeder anderen Instanz können Sie über den instanziierten Wert nicht auf die Statik für diesen Typ zugreifen.Hier ist ein Beispiel dafür, was zu tun ist:

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()
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top