2010-12-15 5 views
2

Ich schreibe einen einfachen Proof of Concept für was im Wesentlichen ein Parser-Generator sein sollte.Können Sie eine Funktion zurückgeben, die auf einem generischen Typ basiert?

Grundsätzlich suche ich nach einer Möglichkeit, dass ich eine Funktion schreiben können, eine Funktion, die aus einem String konvertiert auf ein Objekt eines bestimmten Typs zurück - Ich möchte im Wesentlichen folgende Aufgaben zu:

Func<string, double> ConvertToDouble = BuildConverter(typeof(0.0)); 

Offensichtlich ist dies ein ziemlich konstruiertes Beispiel - aber wenn ich die einfache Version machen kann, dann sollte ich in der Lage sein, die kompliziertere Version zu machen!

FWIW, was ich letztendlich versuche zu tun ist eine Reihe von Werten auf eine Klasse abzubilden, aber um es so flexibel wie möglich zu machen, möchte ich es tun, indem ich eine Funktion habe, die eine Funktion zurückgibt die Umwandlung. Funktional denke, ich ich etwas, das wie folgt aussieht:

a -> (string -> a) 

Als ersten Versuch, ich habe versucht das zu tun:

public static Func<string, T> BuildParser<T>(T t) 
    { 
     if (t is String) 
     return new Func<string, T>(x => x.ToString()); 
     if (t is double) 
     return new Func<string, T>(x => double.Parse(x)); 
    } 

Welche überhaupt nicht funktionieren, aber es Ich fühle mich ein wenig fest, was für eine Herangehensweise ich wählen sollte - also würde jede Hilfe sehr geschätzt werden!

+3

Sie scheinen den Punkt von Generika zu fehlen, wenn Sie für _types_ in Ihrer generische Funktion suchen. – Oded

+2

Wenn Sie Laufzeitentscheidungen möchten, sollten Sie keine Generika verwenden. Wenn Sie Entscheidungen zur Kompilierungszeit treffen möchten, sollten Sie nicht "ist" verwenden. –

+0

@Oded - Ich bin mir nicht sicher, ob ich dem zustimme, ich suche nur nach einer bestimmten Art von Typ-Polymorphie. – MrBear

Antwort

0

können Sie nicht class mit struct Typen mischen. Darüber hinaus wird es funktionieren.

Siehe den Code unten:

private void Testing() { 
    var func = BuildParserStruct<double>(); 
    double value = func("5"); 
} 

public static Func<string, T> BuildParserClass<T>() where T : class 
{ 
    return x => x as T; 
} 

public static Func<string, T> BuildParserStruct<T>() where T : struct 
{ 
    return (x => (T)Convert.ChangeType(x, typeof(double))); 
} 
+0

Das sieht interessant aus! Ich werde es versuchen - ich habe die 'where T:' Syntax völlig vergessen. – MrBear

+0

Was lässt Sie denken, dass Sie "Klasse" nicht mit "struct" -Typen "mischen" können? Dies kann mit einer einzigen 'BuildParser ' Methode erfolgen; Sie benötigen keine separate 'BuildParserClass ' und 'BuildParserStruct ' Methoden, wenn Sie nicht möchten. (Siehe meine Antwort für ein Beispiel.) – LukeH

+0

Fair genug. Was ich hätte sagen sollen ist, dass eine generische Methode sowohl Struktur- als auch Klassentypen (oder abgeleitete Typen) nicht ohne Reflektion instanziieren kann. Diese Einschränkung hat keine Auswirkungen auf Ihre Lösung, da Ihre generische Methode nichts instanziiert ... Ich fand Ihre Lösung clever, wenn auch begrenzt (würde bei 30 verschiedenen Typen einen Cache von 30 Elementen benötigen, und so weiter)) ... – rsenna

0

Ich vermute, dass Sie bestimmte Verhaltensweisen zur Kompilierzeit überprüft werden möchten. Warum nicht einfach Convert anrufen oder individuelle Methoden schreiben? Alles, was Ihre if-Anweisungen erreichen, ist die Rolle des Programmierers, der die geeignete Konvertierungsmethode auswählen sollte.

Wenn Sie zur Laufzeit ausgewählte Verhaltensweisen möchten, sollten Sie Func<string, object> zurückgeben und die Methode nicht-generisch machen.

Das Problem mit einem generischen Typ T in der Methode ist, dass T für jeden Aufruf der Methode festgelegt ist, und die Logik in der Methode T in einem einzelnen Aufruf variieren soll (in einem Fall T ist ein String, In einem anderen Fall ist T eine Dezimalzahl. Der Compiler kann das nicht aussortieren - es müsste beiden rückgabefähigen Instanzen denselben Typ geben.

+0

Sie haben Recht, soweit mein Beispiel geht - es ist nur ein Beweis für das Konzept.Letztendlich habe ich ein Nachrichtensystem, das Nachrichten als kommagetrennte Zeichenfolgen sendet und jede Nachricht einer Klasse (oder einer Struktur) zuordnen kann. Dies bedeutet, dass, wenn jemand hier eine App schreibt, die dieses Nachrichtensystem verwendet, sie Code codieren müssen, wie sie ihn der Klasse zuordnen. Was ich tun möchte, ist im Wesentlichen, das zu automatisieren, indem man sozusagen einen Mapper-Generator schreibt. – MrBear

0

Ich bin mir nicht sicher, was genau Sie tun möchten, aber würde so etwas helfen?

var stringParser = GetParser<string>(); 
string s = stringParser("test"); 

var doubleParser = GetParser<double>(); 
double d = doubleParser("42"); 

// ... 

public static Func<string, T> GetParser<T>() 
{ 
    return (Func<string, T>)_parserCache[typeof(T)]; 
} 

private static readonly Dictionary<Type, Delegate> _parserCache = 
    new Dictionary<Type, Delegate> 
     { 
      { typeof(string), new Func<string, string>(x => x) }, 
      { typeof(double), new Func<string, double>(x => double.Parse(x)) } 
      // etc 
     }; 
0

ADO.Net hat eine Skalarfunktion ausführen, die mich immer gestört, weil es ein Objekt zurückgibt. Sie können eine generische Wrapperfunktion schreiben, um den entsprechenden Typ zurückzugeben. Das setzt natürlich voraus, dass Sie wissen, welcher Typ zurückgegeben wird.

Etwas vereinfacht:

public T ExecuteScalar<T>() 
    { 
     return (T)Command.ExecuteScalar(); 
    } 
Verwandte Themen