2010-12-02 25 views
2

J'ai une méthode qui calcule une liste. À certains points de l'algorithme, un seul élément de la liste doit être choisi. Peu importe quel élément est choisi, mais je voudrais laisser à l'utilisateur de décider.Comment spécifier une méthode de sélection de liste?

En ce moment, j'ai ajouté une méthode d'extension IList<T>.Random() qui prend simplement un élément aléatoire. .First() aurait aussi bien fonctionné. Supposons que je veuille laisser l'utilisateur choisir quelle méthode est utilisée, ou peut-être une méthode entièrement différente, à quoi cela ressemblerait-il?

Je pensais utiliser une énumération avec des options limitées, et ensuite je pourrais envelopper chacun de ces appels dans un commutateur et appeler la fonction appropriée. Mais peut-être some sort of lambda function serait plus approprié? Cette méthode doit être utilisée à deux endroits différents, une fois sur un List<char> et une fois sur un List<string>. Je veux utiliser la même méthode pour les deux.


Ceci n'est pas une application graphique. J'essaie de décider comment concevoir l'API.

Plus précisément, je veux avoir un champ comme

public Func<IList<T>, T> SelectElement = list => list.First(); 

qui serait ensuite utilisée dans la méthode,

public string Reverse(string pattern, IList<object> args = null, IDictionary<string, object> kwargs = null) 

Mais champs génériques ne sont pas possibles. Donc je cherche une solution alternative. Un serait de faire de la méthode SelectElement un argument à Reverse(), alors je pourrais le rendre générique ... mais j'espérais le garder au niveau de la classe pour la réutilisation. Je ne veux pas passer plus d'arguments à la fonction si je peux l'aider.

Edit:full source code

+0

Quelle est votre moyen client? Je dirais que DropDownList ou RadioButtonList serait approprié. –

+0

@Nathan: Ce n'est pas une application gui. Mis à jour Q. – mpen

+0

J'ai lu la question trois fois et ne comprends toujours pas ce que vous demandez :) vous avez une liste d'objets que vous voulez trier et ... ici vous me perdez ... et vous voulez que l'utilisateur choisir comment trier cette liste? précisez s'il vous plaît. – akonsu

Répondre

0
public Func<IList<object>, object> SelectElement = list => list.First(); 

private T _S<T>(IEnumerable<T> list) 
{ 
    return (T)SelectElement(list.Cast<object>().ToList()); 
} 

je peux faire le travail de méthode anonyme sur des objets, ce qui évite les médicaments génériques, puis ajoutez une méthode d'assistance qui est ce que je vais utiliser effectivement l'appeler. Un peu moche, mais semble fonctionner.

+0

Ce n'est pas entièrement optimal en raison des problèmes de boxe et de déballage avec les types de valeur. –

+0

@Joshua: Je sais. C'est pourquoi j'ai dit "c'est un peu moche". Cependant, c'est la seule solution réelle que j'ai découverte jusqu'ici. – mpen

1

Voici un exemple extrêmement basique que je mets ensemble en utilisant une méthode générique qui prend un Func<IEnumerable<T>, T> pour sélectionner un élément de la liste puis renvoie le résultat. Je l'ai fait quelques exemples de la façon de l'appeler:

using System; 
using System.Collections.Generic; 
using System.Linq; 

namespace Test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      //Simple list. 
      var list = new List<int> { 1, 2, 3, 4 }; 

      // Try it with first 
      var result = DoItemSelect(list, Enumerable.First); 
      Console.WriteLine(result); 

      // Try it with last 
      result = DoItemSelect(list, Enumerable.Last); 
      Console.WriteLine(result); 

      // Try it with ElementAt for the second item (index 1) in the list. 
      result = DoItemSelect(list, enumerable => enumerable.ElementAt(1)); 
      Console.WriteLine(result); 
     } 

     public static T DoItemSelect<T>(IEnumerable<T> enumerable, Func<IEnumerable<T>, T> selector) 
     { 
      // You can do whatever you method does here, selector is the user specified func for 
      // how to select from the enumerable. Here I just return the result of selector directly. 
      return selector(enumerable); 
     } 
    } 
} 

Si vous souhaitez limiter les choix d'un utilisateur a, vous pouvez suivre l'itinéraire d'un ENUM et rendre cette méthode une méthode privée et un moyen convertir l'énumération en un délégué de sélection approprié pour passer à la méthode privée sous-jacente.

+0

Nice, mais ne fonctionne pas. Il y a un piège: "Cette méthode doit être utilisée à deux endroits différents, une fois sur une liste et une fois sur une liste " - peut-être que j'aurais dû dire * dans la même méthode *. Ce qui signifie que 'T' ne peut pas être calculé. Ainsi, dans votre exemple, il y aurait en fait deux énumérables utilisés dans 'DoItemSelect', un' List 'et un' List ', qui doivent tous deux être passés dans' selector'. – mpen

+1

Oui, je peux voir où le problème se pose maintenant. Cela nécessitera un peu plus de réflexion pour trouver une solution optimale. –

2

que diriez-vous:


    public class MyClass 
    { 
     public static class C<T> 
     { 
      public static Func<IList<T>, T> SelectElement; 
     } 

     public int Test(IList<int> list) 
     { 
      return C<int>.SelectElement(list); 
     } 
    } 

    static class Program 
    { 
     static void Main(string[] args) 
     { 
      MyClass.C<char>.SelectElement = xs => xs.First(); 
      MyClass.C<int>.SelectElement = xs => xs.First(); 

      var list = new List<int>(new int[] { 1, 2, 3 }); 

      var c = new MyClass(); 

      var v = c.Test(list); 
      Console.WriteLine(v); 
     } 
    } 
+0

Vous avez des erreurs de syntaxe ... et où 'xs' est-il défini? De même, comment l'utilisateur (programmeur) changerait-il "SelectElement"? Il n'y a pas de setter. De plus, vous ne pouvez pas avoir de propriétés génériques. – mpen

+0

Je n'ai pas d'erreurs de syntaxe, il compile. Peut-être que j'ai oublié d'échapper à un crochet gauche ou deux. Pardon. – akonsu

+0

vous pouvez avoir des propriétés génériques. si la classe a le paramètre T comme argument. – akonsu

0

Cela fonctionne pour les caractères et les chaînes. N'a pas testé avec d'autres types. Construis cela avant que je voie le code de Ralph, qui est pratiquement le même.

Code LINQPad:

void Main() 
{ 
    var chars = new List<char>(); 
    var strings = new List<string>(); 

    chars.AddRange(new char[] {'1','2','4','7','8','3'}); 
    strings.AddRange(new string[] {"01","02","09","12","28","52"}); 

    chars.Dump(); 
    strings.Dump(); 

    Func<IList<object>, string> SelectFirst = (list) 
     => list.First().ToString(); 
    Func<IList<object>, string> SelectLast = (list) 
     => list.Last().ToString(); 
    Func<IList<object>, string> SelectRandom = (list) 
     => list.ElementAt(new Random().Next(0, list.Count())).ToString(); 

    SelectBy(SelectFirst, strings.Cast<object>().ToList()).Dump(); 
    SelectBy(SelectFirst, chars.Cast<object>().ToList()).Dump(); 

    SelectBy(SelectLast, strings.Cast<object>().ToList()).Dump(); 
    SelectBy(SelectLast, chars.Cast<object>().ToList()).Dump(); 

    SelectBy(SelectRandom, strings.Cast<object>().ToList()).Dump(); 
    SelectBy(SelectRandom, chars.Cast<object>().ToList()).Dump(); 
} 

private string SelectBy(Func<IList<object>, string> func, IList<object> list) 
{ 
    return func(list); 
}