2009-03-04 6 views
5

J'écris une fonction de filtre pour renvoyer le type spécifique spécifié d'une plus grande collection de supertypes (objets par exemple). L'idée est que je vous donne un énumérable et que vous me rendiez toutes les cordes par exemple. vous pouvez l'écrire de cette façon sans médicaments génériques:Quelle est la manière appropriée de taper fortement le retour d'une fonction générique?

public static IEnumerable Filter(IEnumerable source, Type type) 
{ 
    List<object> results = new List<object>(); 

    foreach(object o in source) 
    { 
     if(o != null && o.GetType() == type) 
     { 
      results.Add(o); 
     } 
    } 

    return results; 
} 

si nous voulons revenir génériques, il y a plusieurs façons de s'y prendre.

En tant que port droit:

public static IEnumerable<TResult> Filter<TResult> 
          (IEnumerable source, Type type) 

passe dans un 'exemple':

IEnumerable<TResult> Filter<TResult> 
    (IEnumerable source, TResult resultType) 

En fin de compte ce que je pense est plus propre:

public static IEnumerable<T> Filter<T>(IEnumerable source) 

Le second type serait appelé entièrement avec des paramètres (et déduire le type):

Filter(myList, "exampleString"); 

alors que la version finale, il obtiendrait appelé avec un spécificateur de type:

Filter<string>(myList); 

Quelle est la manière appropriée de typer le retour d'une fonction générique, où le type de retour n'implique pas automatiquement dans la signature? (Pourquoi?)

(Edit Note: Notre entrée n'est pas typé, par exemple IEnumerable <T> Au mieux, il serait IEnumerable Cette fonction retourne le Ts de toute la collection d'autres types...)

Répondre

9

La méthode d'extension suivante inclus dans Linq fait exactement ce dont vous avez besoin:

IEnumerable<T> OfType<T>(this IEnumerable enumerable); 

Voici un exemple d'utilisation:

List<object> objects = //... 

foreach(string str in objects.OfType<string>()) 
{ 
    //... 
} 

Comme vous pouvez le voir, ils ont utilisé le paramètre générique comme spécificateur de type de retour. C'est plus simple et plus sûr que d'utiliser un type ou une chaîne et renvoyer une énumération non typée.

+1

Ainsi, la manière « .net » semble être spécifier le type, ce qui est bon de savoir, je vous remercie. Malheureusement, nous sommes dans un monde 2.0, et nous avons de la chance d'avoir des génériques. –

+0

Oui, je comprends, nous étions coincés avec 2,0 pendant un certain temps ici aussi. Je suppose que le plus proche que vous pourriez obtenir .Net est votre dernière solution. La seule chose manquante serait la méthode d'extension. – Coincoin

+0

J'ai marqué cela comme accepté à cause de la source citée, bonne pensée. –

1

La manière la plus propre serait

public static IEnumerable<T> Filter<T>(IEnumerable<T> source) 

Cela supprime toutes les fonctions de sécurité non de type. Vous pouvez ensuite convertir tout en IEnumerable une version générique non générique avec un appel coulé

IEnumerable enumerable = GetMyEnumerable(); 
var filtered = Filter(enumerable.Cast<string>()); 

Vous pouvez aussi faire en outre, il une méthode d'extension et de faire l'appel encore plus rationaliser.

public static IEnumerable<T> Filter<T>(this IEnumerable<T> source) 
... 
var filtered = GetMyEnumerable().Cast<string>().Filter(); 

EDIT

OP mentionné qu'ils ne veulent que de filtrer à des types spécifiques. Dans ce cas, vous pouvez simplement utiliser Enumerable.OfType

var filtered = GetMyEnumerable().OfType<SomeType>(); 
+0

Vous avez oublié le mot-clé 'this' - vous l'avez corrigé. –

+0

@Joel merci! Je l'ai même ajouté deux fois pour ajouter cela et montrer la différence. – JaredPar

+0

Je vais mettre à jour le premier message pour être plus clair, mais nous traitons des énumérations de types mélangés. IEnumerable au mieux. Nous filtrons juste les Ts dans l'énumération. –

4

Je préfère généralement la version finale - elle précise toutes les informations pertinentes et rien d'autre. Compte tenu de la version avec des paramètres, si vous étiez nouveau au code ne vous attendez la valeur du paramètre à un sens, plutôt que le type?

Très rarement ce modèle « paramètre fictif » est utile, mais je steer généralement claire - ou à tout le moins fournir une surcharge qui ne l'exigent.

+0

Jon, Merci d'avoir répondu à la question philosophique verbalement! Si je pouvais marquer deux réponses comme «acceptées», ce serait la seconde. L'autre a cité le code MS dans LINQ, qui fait plus autorité, mais j'apprécie aussi la discussion! Je vous remercie. –

0

ceci est mon 2 cents. Je suis un peu confus à propos de ce que vous essayez de filtrer. IEnumerable est non générique, alors comment allez-vous filtrer une source non générique et retourner un résultat de IEnuerable.

Je pense que la plus propre est

public static IEnumerable<T> Filter<T>(IEnumerable<T> source) 

vous pouvez même mettre des contrôles de type générique, si vous savez quel genre de types de votre filtre T sera, par exemple seulement des classes ou des objets d'une certaine interface ou classe de base, ou des types de valeur.

public static IEnumerable<T> Filter<T>(IEnumerable<T> source) where T : class 
public static IEnumerable<T> Filter<T>(IEnumerable<T> source) where T : struct 
+0

J'ai mis à jour le premier message pour être plus clair. Je prends les Ts d'une collection d'objets. Au mieux, il serait IEnumerable sur l'entrée. Vous ne pouvez pas déduire le retour de cela, d'où la question. –

1

Si vous utilisez Framework 3.5, ce qui est déjà mis en œuvre dans IEnumerable:

IEnumerable<string> s = someList.OfType<string>() 
0

Il est clair (pour moi, au moins) que vous voudriez la version finale:

IEnumerable<T> Filter<T>(IEnumerable source) 

par le processus de réduction, si rien d'autre.

La première version:

IEnumerable<T> Filter<T>(IEnumerable source, Type type) 

doit faire face à la folie où je passe un type qui ne correspond pas à la contrainte:

La deuxième version:

IEnumerable<T> Filter<T>(IEnumerable source, T type) 

me fait Construire un exemple, qui peut être coûteux ou autrement indisponible pour la construction. Aussi, que faire si je passe en null (dans les deux cas)? En aparté, pour les contraintes simples, je pense que le paramètre type devrait être T. Vous pouvez vous en sortir avec TResult si c'est le retour d'une fonction - comme Func <TResult>, mais sinon c'est juste plus typer avec moins de clarté .

0

Je pense que cela est à peu près ce que vous voulez.

public static IEnumerable<T> OfType<T>(IEnumerable source) { 
    foreach (object obj in source) 
     if (obj is T) 
      yield return (T)obj; 
} 

Une version un peu plus complexe, mais (probablement) un peu plus rapide serait

public static IEnumerable<T> OfType<T>(IEnumerable source) { 
    foreach (object obj in source) { 
     T result = obj as T; 
     if (result != null) 
      yield return result; 
    } 
} 
+0

Utilisez comme et enregistrez la distribution en double: http://msdn.microsoft.com/fr-fr/library/cscsdfbt(VS.71).aspx –

Questions connexes