Generics in C# & Zugriff auf die statischen Mitglieder von T


12

Meine Frage betrifft C# und wie man auf statische Mitglieder ... Nun, ich weiß nicht wirklich, wie man es erklärt (was für eine Frage schlecht ist, ist es nicht ?) ich werde Sie nur einige Beispiel-Code geben: wie ich dieses Problem lösen würde einen Fehler, ohne sich):

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

die Frage ist also danke euch für eure Antworten (Übrigens. Das ist wahrscheinlich eine ziemlich einfache Frage für Sie!

Danke, Niklas


Edit: Vielen Dank für Ihre Antworten!

Obwohl ich denke, der Versuch - Fang Ausdruck ist der eleganteste, ich weiß aus meiner Erfahrung mit vb, dass es wirklich ein Mist sein kann. Ich habe es einmal benutzt und es dauerte ungefähr 30 Minuten, um ein Programm zu starten, das später nur 2 Minuten dauerte, um zu berechnen, nur weil ich Versuch - Fang vermied.

Deshalb wählte ich die swich-Anweisung als beste Antwort. Es macht den Code komplizierter, aber auf der anderen Seite stelle ich mir vor, dass es relativ schnell und relativ einfach zu lesen ist. (Obwohl ich denke immer noch, soll es eine elegantere Möglichkeit sein ... vielleicht in der nächsten Sprache Ich lerne: P)


Obwohl, wenn Sie einigen anderen Vorschlag, den ich noch warte (und zur Teilnahme bereit)

2

Eine weitere Möglichkeit, es diesmal einige Reflexionen in der Mischung zu tun:

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

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

Mit voller Parametertyp Anpassung implementieren würde versuchen, etc

  0

Das ist ungött langsam. Sie sollten einen generischen Delegaten zwischenspeichern. 03 jan. 112011-01-03 16:13:24


5

Das Problem ist, dass TryParse nirgendwo auf einer Schnittstelle oder Basisklasse definiert ist, so dass Sie keine Annahme machen können, dass der in Ihre Klasse übergebene Typ diese Funktion haben wird. Es sei denn, Sie können T auf irgendeine Weise widersprechen, Sie werden viel davon erfahren.

Constraints on Type Parameters


0

Sie wahrscheinlich kippe es tun.

Als erstes sollte es möglich sein, dass Sie eine engere Bindung an T benötigen, damit der Typchecker sicher sein kann, dass alle möglichen Substitutionen für T tatsächlich eine statische Methode namens TryParse hatten.


-1

So funktioniert Statik nicht. Sie müssen Statik als eine Art in einer globalen Klasse betrachten, auch wenn sie über eine ganze Reihe von Typen verteilt sind. Ich empfehle, es zu einer Eigenschaft innerhalb der T-Instanz zu machen, die auf die erforderliche statische Methode zugreifen kann.

Auch T ist eine tatsächliche Instanz von etwas, und genau wie jede andere Instanz können Sie nicht über den instanziierten Wert 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() 
} 

3

Für den Zugriff auf ein Mitglied einer bestimmten Klasse oder Schnittstelle benötigen Sie das Wo Schlüsselwort verwenden und die Schnittstelle oder Basisklasse angeben, die das Verfahren hat.

In der obigen Instanz kommt TryParse nicht von einer Schnittstelle oder Basisklasse, also was Sie versuchen, oben zu tun, ist nicht möglich. Am besten verwenden Sie 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; 
     } 
    } 
} 

0

Vielleicht möchten Sie meine vorherige Post auf limiting generic types to primitives lesen.Dies kann Ihnen einige Zeiger in der Begrenzung des Typs geben, der an die generische übergeben werden kann (da TypeParse ist offensichtlich nur für eine bestimmte Anzahl von Primitiven verfügbar (string.TryParse offensichtlich die Ausnahme, die keinen Sinn macht) .

Sobald Sie eher ein Griff auf die Art haben, können Sie dann zu versuchen, arbeiten, um es zu analysieren. Sie in ein bisschen ein hässliches Schalter müssen (die richtige TryParse nennen), aber ich glaube, Sie kann die gewünschte Funktionalität erreichen

Wenn Sie mich brauchen, um etwas von den oben genannten weiter zu erklären, dann bitte fragen :)


1

Sie meinen, so etwas zu 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 für das TryParse Muster überprüfen kann, wie es statisch ist - was leider bedeutet, dass es nicht besonders gut geeignet für Generika ist.


1

Der einzige Weg, genau das zu tun, was Sie suchen für die Reflexion zu verwenden wäre zu prüfen, ob das Verfahren für T. existiert

Eine weitere Möglichkeit sicherzustellen ist, dass das Objekt, das Sie in schicken ist ein Cabrio-Objekt durch Beschränken des Typs auf IConvertible (alle primitiven Typen implementieren IConvertible). Dies würde es Ihnen ermöglichen, Ihren Parameter sehr flexibel auf den gegebenen Typ zu konvertieren.

Sie können auch eine Variation vornehmen, indem Sie einen "Objekt" -Typ verwenden, anstatt wie ursprünglich.

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

3

Kurze Antwort, können Sie nicht.

Lange Antwort, können Sie betrügen:

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 einen Delegaten für die TryParse Verfahren jeder Art hält Ich möchte vielleicht verwenden. Ich schrieb dann eine generische Methode, um das Wörterbuch nachzuschlagen und den Anruf an den passenden Delegierten weiterzugeben. Da jeder Delegat einen anderen Typ hat, speichere ich sie einfach als Objektreferenzen und wandle sie beim Generieren auf den entsprechenden generischen Typ zurück. Beachten Sie, dass ich aus Gründen eines einfachen Beispiels die Fehlerüberprüfung weggelassen habe, z. B. um zu prüfen, ob wir einen Eintrag im Wörterbuch für den gegebenen Typ haben.


2

Ok Jungs: Danke für den ganzen Fisch. Jetzt mit Ihren Antworten und meinen Recherchen (vor allem den Artikel auf limiting generic types to primitives) werde ich Ihnen meine Lösung vorstellen.

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

0

Bester Code: Beschränke 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, Enums sind alle Werttypen.

btw der Compiler akzeptiert keine statische Methoden oder Zugriff auf statische Member auf ‚Typ Parameter‘ zu nennen, wie im folgenden Beispiel, die nicht kompilieren :(

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

=> Fehler 1 ‚T‘ ist ein ‚Typ-Parameter‘, die nicht gültig im gegebenen Kontext ist

SL


1

das ist nicht wirklich eine Lösung, aber in bestimmten Szenarien könnte es eine gute Alternative sein. Wir können einen zusätzlichen Delegierten zum Pass generische Methode

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

So jetzt würden wir tun müssen:

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

Allerdings konnten wir Ändern Sie unsere Methode, um einen zusätzlichen Delegierten zu übernehmen:

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 Anruf ändern:

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

Natürlich ist dies nur in ganz bestimmten Szenarien nützlich. Aber das ist die sauberste Lösung in dem Sinne, dass wir Kompilierzeit-Sicherheit bekommen, es gibt kein "Hacking", und der Code ist tot einfach.