3 votes

options disponibles pour les "faux" mots-clés de syntaxe de compréhension linq en C# ?

Bien qu'il y ait quelques cas où j'écrirai quelque chose en utilisant les chaînes de méthodes (surtout s'il ne s'agit que d'une ou deux méthodes, comme foo.Where(..).ToArray()), dans de nombreux cas, je préfère la syntaxe de compréhension des requêtes LINQ ("expressions de requêtes" dans la spécification) :

var query =
    from filePath in Directory.GetFiles(directoryPath)
    let fileName = Path.GetFileName(filePath)
    let baseFileName = fileName.Split(' ', '_').First()
    group filePath by baseFileName into fileGroup
    select new
    {
        BaseFileName = fileGroup.Key,
        Count = fileGroup.Count(),
    };

Dans un nombre assez important d'entre eux, je dois prendre le IEnumerable résultant et le charger dans une structure de données (tableau, liste, etc.). Cela signifie généralement soit :

  1. ajouter une autre variable locale comme var queryResult = query.ToArray() ; ou

  2. envelopper la requête avec des parenthèses et la baliser avec ToArray (ou ToList ou autre).

    var query = ( from filePath in Directory.GetFiles(directoryPath) let fileName = Path.GetFileName(filePath) let baseFileName = fileName.Split(' ', '_').First() group filePath by baseFileName into fileGroup select new { BaseFileName = fileGroup.Key, Count = fileGroup.Count(), } ).ToArray();

J'essaie de trouver quelles sont les options que d'autres 1) utilisent déjà ou 2) pourraient considérer comme faisables pour avoir des "mots-clés contextuels" supplémentaires - juste des choses qui se transformeraient en méthodes d'extension de la même manière que les méthodes existantes, comme si les mots-clés LINQ étaient "nativement" extensibles :)

Je me rends compte que cela va probablement impliquer soit une sorte de prétraitement (je ne suis pas sûr de ce qui existe dans ce domaine pour C#), soit une modification du compilateur utilisé pour quelque chose comme Nemerle (Je pense que ce serait une option, mais je n'en suis pas vraiment sûr ). Je n'en sais pas encore assez sur ce que Roslyn supporte/supportera, donc si quelqu'un sait s'il pourrait permettre à quelqu'un d'"étendre" C# de cette façon, merci de nous le dire !

Ceux que j'utiliserais probablement le plus (bien que je sois sûr qu'il y en ait beaucoup d'autres, mais juste pour faire passer l'idée / ce que j'espère) :

compte - se transforme en Count()

int zFileCount =
    from filePath in Directory.GetFiles(directoryPath)
    where filePath.StartsWith("z")
    select filePath ascount;

Cela se "transformerait" (peu importe le chemin, tant que le résultat final l'est) en :

int zFileCount = (
    from filePath in Directory.GetFiles(directoryPath)
    where filePath.StartsWith("z")
    select filePath
).Count();

De même :

  • asarray - se transforme en ToArray()
  • aslist - se transforme en ToList()

(vous pourriez évidemment continuer à utiliser First(), Single(), Any(), etc., mais j'essaie de limiter l'étendue des questions :)

Je ne suis intéressé que par les méthodes d'extension qui ne nécessitent pas le passage de paramètres. Je ne cherche pas à essayer de faire ce genre de choses avec (par exemple) ToDictionary ou ToLookup. :)

Donc, en résumé :

  • je veux ajouter 'ascount', 'aslist' et 'asarray' dans les expressions de requêtes linq.
  • Je ne sais pas si cette question a déjà été résolue.
  • je ne sais pas si Nemerle est un bon choix pour cela
  • Je ne sais pas si l'histoire de Roslyn supporterait ce genre d'utilisation.

13voto

Eric Lippert Points 300275

Ce n'est pas une réponse à votre question, mais plutôt quelques réflexions sur votre conception. Nous avons fortement envisagé d'ajouter une telle fonctionnalité à C# 4, mais nous avons renoncé parce que nous n'avions pas le temps et les ressources nécessaires.

Le problème avec la syntaxe de compréhension des requêtes est, comme vous le notez, qu'il est laid de mélanger les syntaxes "fluide" et "compréhension". Vous voulez savoir combien de noms de famille différents ont vos clients à Londres et vous finissez par écrire ce truc moche avec des parenthèses :

d = (from c in customers 
     where c.City == "London" 
     select c.LastName)
    .Distinct()
    .Count();

Beurk.

Nous avons envisagé d'ajouter un nouveau mot clé contextuel à la syntaxe de la compréhension. Disons, pour les besoins de l'argumentation, que ce mot-clé est "avec". Vous pourriez alors dire :

d = from c in customers 
    where c.City == "London" 
    select c.LastName
    with Distinct() 
    with Count();

et le réécrivant de la compréhension des requêtes réécrirait cela dans la syntaxe fluide appropriée.

J'aime beaucoup cette fonctionnalité mais elle n'a pas été retenue pour C# 4 ou 5. Ce serait bien de l'intégrer dans une future version hypothétique du langage.

Comme toujours, les réflexions d'Eric sur les caractéristiques hypothétiques de produits non annoncés qui pourraient ne jamais exister sont uniquement destinées à divertir.

3voto

Kevin Pilch-Bisson Points 4570

L'idée est que vous pourriez écrire votre propre fournisseur de requêtes qui englobe la version en System.Linq et appelle ensuite ToArray dans son Select méthode. Vous n'auriez alors qu'un using YourNamespace; au lieu de using System.Linq .

Roslyn ne vous permet pas d'étendre la syntaxe de C#, mais vous pouvez écrire un fichier SyntaxRewriter qui change la sémantique d'un programme C# comme une étape de reconstruction.

2voto

svick Points 81772

Comme d'autres l'ont dit, Roslyn n'est pas ce que vous pensez probablement. Il ne peut pas être utilisé pour étendre C#.

Tout le code suivant doit être considéré comme un brainstorming et non comme une recommandation. Il modifie le comportement de LINQ de manière inattendue et vous devriez réfléchir sérieusement avant d'utiliser un tel code.

Une façon de résoudre ce problème consisterait à modifier l'interface de l'utilisateur. select clause comme celle-ci :

int count = from i in Enumerable.Range(0, 10)
            where i % 2 == 0
            select new Count();

La mise en œuvre pourrait ressembler à ceci :

public  class Count
{}

public static class LinqExtensions
{
    public static int Select<T>(
        this IEnumerable<T> source, Func<T, Count> selector)
    {
        return source.Count();
    }
}

Si vous mettez quelque chose qui n'est pas Count dans le select il se comportera comme d'habitude.

Faire quelque chose de similaire pour les tableaux demanderait plus de travail, puisque vous avez besoin de l'option select pour spécifier à la fois que vous voulez un tableau et le sélecteur d'éléments que vous voulez y mettre, mais c'est faisable. Ou vous pouvez utiliser deux select s : l'un choisit l'élément et l'autre dit que vous voulez un tableau.

Une autre option (similaire à la suggestion de Kevin) serait d'avoir une méthode d'extension telle que AsCount() que vous pouvez utiliser comme ceci :

int count = from i in Enumerable.Range(0, 10).AsCount()
            where i % 2 == 0
            select i;

Vous pourriez le mettre en œuvre comme suit :

public static class LinqExtensions
{
    public static Countable<T> AsCount<T>(this IEnumerable<T> source)
    {
        return new Countable<T>(source);
    }
}

public class Countable<T>
{
    private readonly IEnumerable<T> m_source;

    public Countable(IEnumerable<T> source)
    {
        m_source = source;
    }

    public Countable<T> Where(Func<T, bool> predicate)
    {
        return new Countable<T>(m_source.Where(predicate));
    }

    public Countable<TResult> Select<TResult>(Func<T, TResult> selector)
    {
        return new Countable<TResult>(m_source.Select(selector));
    }

    // other LINQ methods

    public static implicit operator int(Countable<T> countable)
    {
        return countable.m_source.Count();
    }
}

Je ne suis pas sûr de l'aimer comme ça. En particulier, la distribution implicite ne me semble pas correcte, mais je pense qu'il n'y a pas d'autre moyen.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X