2010-12-15 129 views
2

J'écris une preuve de concept simple pour ce qui devrait être, par essence, un générateur d'analyseur.Pouvez-vous retourner une fonction basée sur un type générique?

Fondamentalement, je suis à la recherche d'une façon que je peux écrire une fonction qui retourne une fonction qui convertit d'une chaîne à un objet d'un type donné - je veux être en mesure de faire ce qui suit en substance:

De toute évidence, il s'agit d'un exemple assez artificiel - mais si je peux faire la version simple alors je devrais être en mesure de faire la version plus compliquée! FWIW, ce que j'essaie finalement de faire est de mapper une chaîne de valeurs sur une classe, mais pour la rendre aussi flexible que possible, je veux le faire en ayant une fonction qui retournera une fonction qui fait La conversion. Sur le plan fonctionnel, je pense que je veux quelque chose qui ressemble à ceci:

a -> (string -> a) 

En tant que premier essai, je l'ai essayé de faire ceci:

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

Ce qui ne fonctionne pas du tout, mais Je me sens un peu coincé quant à l'approche que je devrais adopter - toute aide serait grandement appréciée!

+3

Vous semblez manquer le point de génériques si vous cherchez _types_ dans votre fonction générique. – Oded

+2

Si vous voulez des décisions d'exécution, vous ne devez pas utiliser de génériques. Si vous voulez des décisions à la compilation, vous ne devriez pas utiliser "est". –

+0

@Oded - Je ne suis pas sûr d'être d'accord, je cherche juste un type particulier de polymorphisme de type. – MrBear

Répondre

0

Vous ne pouvez pas mélanger class avec struct types. Au-delà, ça va marcher.

Voir le code ci-dessous:

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

Cela semble intéressant! Je vais essayer - j'ai complètement oublié la syntaxe 'where T:'. – MrBear

+0

Qu'est-ce qui vous fait penser que vous ne pouvez pas "mélanger' class' avec des types 'struct'"? Cela peut être fait avec une seule méthode 'BuildParser '; vous n'avez pas besoin de méthodes séparées 'BuildParserClass ' et 'BuildParserStruct ' si vous ne le souhaitez pas. (Voir ma réponse pour un exemple.) – LukeH

+0

Assez juste. Ce que j'aurais dû dire, c'est qu'une méthode générique ne peut pas instancier à la fois les types struct et class (ou les types dérivés) sans utiliser la réflexion. Cette limitation n'affecte pas votre solution, puisque votre méthode générique n'instancie rien ... J'ai trouvé que votre solution était intelligente, même si elle était limitée (elle aurait besoin d'un cache de 30 éléments pour gérer 30 types différents, et ainsi de suite) ... – rsenna

0

Je suppose que vous voulez que les comportements spécifiques soient vérifiés au moment de la compilation. Pourquoi ne pas simplement appeler Convert ou écrire des méthodes individuelles à utiliser? Tout ce que vos instructions if accomplissent est de prendre le rôle du programmeur qui devrait choisir la méthode de conversion appropriée.

Si vous souhaitez que les comportements soient sélectionnés à l'exécution, vous devez renvoyer Func<string, object> et rendre la méthode non générique. Le problème avec l'utilisation d'un type T générique dans la méthode est que T est fixé pour chaque appel à la méthode, et la logique dans la méthode suppose que T varie en un seul appel (dans un cas, T est une chaîne, dans un autre cas, T est une décimale). Le compilateur ne peut pas trier ceci - il devrait permettre aux deux instances retournables d'avoir le même type.

+0

Vous avez raison dans la mesure où mon exemple va - c'est juste une preuve de concept cependant.En fin de compte, j'ai un système de messagerie qui envoie des messages sous forme de chaînes délimitées par des virgules, et chaque message peut être mappé sur une classe (ou une structure). Cela signifie que chaque fois que quelqu'un écrit une application ici qui utilise ce système de messages, ils doivent coder à la main comment ils le mappent à la classe. Ce que je veux faire, c'est, en substance, automatiser cela en écrivant un générateur de mappeur pour ainsi dire. – MrBear

0

Je ne sais pas exactement ce que vous essayez de faire, mais est-ce que quelque chose comme ça vous aiderait?

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 a une fonction Execute Scalar qui me dérange toujours parce qu'il retourne un objet. Vous pouvez écrire une fonction wrapper générique pour renvoyer le type approprié. Bien sûr, cela suppose que vous savez quel type sera retourné.

Un peu simplifié:

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