Question

Je pensais que ce serait bien de faire quelque chose comme ça (avec le lambda faisant un rendement):

public IList<T> Find<T>(Expression<Func<T, bool>> expression) where T : class, new()
{
    IList<T> list = GetList<T>();
    var fun = expression.Compile();

    var items = () => {
        foreach (var item in list)
            if (fun.Invoke(item))
                yield return item; // This is not allowed by C#
    }

    return items.ToList();
}

Cependant, j'ai découvert que je ne pouvais pas utiliser le rendement en méthode anonyme. Je me demande pourquoi Les documents de rendement indiquent simplement que ce n'est pas autorisé.

Comme cela n’était pas autorisé, je viens de créer une liste et d’y ajouter les éléments.

Était-ce utile?

La solution

Eric Lippert a récemment écrit une série d'articles sur pourquoi le rendement n'est pas autorisé dans certains cas.

EDIT2:

  • Partie 7 (celui-ci a été posté ultérieurement et traite spécifiquement de cette question)

Vous y trouverez probablement la réponse ...

EDIT1: cela est expliqué dans les commentaires de la partie 5, dans la réponse d'Eric au commentaire d'Abhijeet Patel:

Q:

  

Eric,

     

Pouvez-vous également donner un aperçu de   pourquoi " rendements " ne sont pas autorisés à l'intérieur d'un   méthode anonyme ou expression lambda

A:

  

Bonne question. J'aimerais avoir   blocs itérateurs anonymes. Ce serait   totalement génial de pouvoir construire   vous-même un petit générateur de séquence   en place qui a fermé sur local   variables. La raison pour laquelle c'est   simple: les avantages ne sont pas   l'emportent sur les coûts. La génialité de   faire des générateurs de séquence en place est   en fait assez petit dans le grand   schéma de choses et méthodes nominales   faire le travail assez bien dans la plupart   scénarios. Donc, les avantages ne sont pas   cela convaincant.

     

Les coûts sont élevés. Itérateur   la réécriture est la plus compliquée   transformation dans le compilateur, et   méthode anonyme de réécriture est la   deuxième plus compliqué. Anonyme   les méthodes peuvent être à l'intérieur d'autres anonymes   méthodes, et les méthodes anonymes peuvent être   à l'intérieur des blocs d'itérateur. Donc,   ce que nous faisons est d'abord nous réécrivons tous   méthodes anonymes pour qu'ils deviennent   méthodes d'une classe de fermeture. C'est   l'avant-dernière chose du compilateur   fait avant d’émettre IL pour une méthode.   Une fois cette étape effectuée, l’itérateur   rewriter peut supposer qu'il n'y a pas   méthodes anonymes dans l'itérateur   bloc; ils ont tous être réécrits   déjà. Par conséquent, l'itérateur   le rédacteur peut simplement se concentrer sur   réécrire l'itérateur, sans   craignant qu'il pourrait y avoir un   méthode anonyme non réalisée ici.

     

De plus, les blocs d'itérateur ne "nichent" jamais,   contrairement aux méthodes anonymes. L'itérateur   réécrivain peut supposer que tout itérateur   les blocs sont "de premier niveau".

     

Si les méthodes anonymes sont autorisées à   contient des blocs d'itérateur, puis les deux   ces hypothèses vont par la fenêtre.   Vous pouvez avoir un bloc itérateur qui   contient une méthode anonyme qui   contient une méthode anonyme qui   contient un bloc itérateur   contient une méthode anonyme, et ...   beurk. Maintenant, nous devons écrire une réécriture   passe qui peut gérer un itérateur imbriqué   blocs et méthodes anonymes imbriquées à   en même temps, fusionnant nos deux plus   algorithmes compliqués dans un loin   algorithme plus compliqué. Il serait   être vraiment difficile à concevoir, mettre en œuvre,   et test. Nous sommes assez intelligents pour faire   alors j'en suis sûr. Nous avons une équipe intelligente   ici. Mais nous ne voulons pas prendre   ce grand fardeau pour un & quo

Autres conseils

Eric Lippert a écrit une excellente série d'articles sur les limitations (et les décisions de conception influençant ces choix) sur blocs d'itérateur

En particulier, les blocs itérateurs sont implémentés par certaines transformations sophistiquées du code du compilateur. Ces transformations auraient un impact sur les transformations qui se produisent à l'intérieur de fonctions anonymes ou de lambdas, de sorte que, dans certaines circonstances, ils essaient tous deux de "convertir" le code en une autre construction incompatible avec l'autre.

En conséquence, toute interaction leur est interdite.

Le fonctionnement des blocs d'itérateurs sous le capot est traité correctement ici .

Comme exemple simple d'incompatibilité:

public IList<T> GreaterThan<T>(T t)
{
    IList<T> list = GetList<T>();
    var items = () => {
        foreach (var item in list)
            if (fun.Invoke(item))
                yield return item; // This is not allowed by C#
    }

    return items.ToList();
}

Le compilateur veut simultanément convertir ceci en quelque chose comme:

// inner class
private class Magic
{
    private T t;
    private IList<T> list;
    private Magic(List<T> list, T t) { this.list = list; this.t = t;}

    public IEnumerable<T> DoIt()
    {
        var items = () => {
            foreach (var item in list)
                if (fun.Invoke(item))
                    yield return item;
        }
    }
}

public IList<T> GreaterThan<T>(T t)
{
    var magic = new Magic(GetList<T>(), t)
    var items = magic.DoIt();
    return items.ToList();
}

et en même temps l'aspect itérateur essaie de faire son travail pour faire une petite machine à états. Certains exemples simples peuvent fonctionner avec une bonne quantité de contrôle de cohérence (d'abord avec les fermetures imbriquées (éventuellement arbitrairement)), puis de voir si les classes résultantes de très bas niveau peuvent être transformées en machines d'état d'itérateurs.

Cependant, ce serait

  1. Beaucoup de travail.
  2. Impossible de travailler dans tous les cas sans au moins que l'aspect bloc itérateur puisse empêcher l'aspect fermeture d'appliquer certaines transformations pour plus d'efficacité (comme la promotion de variables locales en variables d'instance plutôt qu'en tant que classe de clôture à part entière).
    • S'il existait une faible probabilité de chevauchement là où il était impossible ou suffisamment difficile de ne pas être mis en œuvre, le nombre de problèmes d'assistance en résultant serait probablement élevé, car de nombreux utilisateurs perdraient le changement subtil.
  3. Cela peut être très facilement corrigé.

Dans votre exemple, comme ceci:

public IList<T> Find<T>(Expression<Func<T, bool>> expression) 
    where T : class, new()
{
    return FindInner(expression).ToList();
}

private IEnumerable<T> FindInner<T>(Expression<Func<T, bool>> expression) 
    where T : class, new()
{
    IList<T> list = GetList<T>();
    var fun = expression.Compile();
    foreach (var item in list)
        if (fun.Invoke(item))
            yield return item;
}

Malheureusement, je ne sais pas pourquoi ils ne l'ont pas autorisé, car il est tout à fait possible d'envisager comment cela fonctionnerait.

Cependant, les méthodes anonymes font déjà partie de la "magie du compilateur". en ce sens que la méthode sera extraite soit vers une méthode de la classe existante, soit même vers une toute nouvelle classe, selon qu’il s’agisse ou non de variables locales.

De plus, les méthodes itérateurs utilisant yield sont également implémentées à l'aide de la magie du compilateur.

Je suppose que l'un de ces deux codes rend le code impossible à identifier par l'autre pièce de magie et qu'il a été décidé de ne pas perdre de temps à le faire pour les versions actuelles du compilateur C #. Bien sûr, ce n'est peut-être pas un choix conscient, et cela ne fonctionne tout simplement pas car personne n'a pensé à le mettre en œuvre.

Pour une question précise à 100%, je vous suggère d'utiliser le site Microsoft Connect et de signaler une question, Je suis sûr que vous obtiendrez quelque chose d’utilisable en retour.

Je voudrais faire ceci:

IList<T> list = GetList<T>();
var fun = expression.Compile();

return list.Where(item => fun.Invoke(item)).ToList();

Bien entendu, vous avez besoin de System.Core.dll référencé à partir de .NET 3.5 pour la méthode Linq. Et inclure:

using System.Linq;

A bientôt,

Sly

Peut-être que c'est juste une limitation de syntaxe. Dans Visual Basic .NET, qui est très similaire à C #, il est parfaitement possible d’écrire de manière maladroite

Sub Main()
    Console.Write("x: ")
    Dim x = CInt(Console.ReadLine())
    For Each elem In Iterator Function()
                         Dim i = x
                         Do
                             Yield i
                             i += 1
                             x -= 1
                         Loop Until i = x + 20
                     End Function()
        Console.WriteLine(
static void Main()
{
    Console.Write("x: ");
    var x = System.Convert.ToInt32(Console.ReadLine());
    // ERROR: CS0149 - Method name expected 
    foreach (var elem in () =>
    {
        var i = x;
        do
        {
            yield return i;
            i += 1;
            x -= 1;
        }
        while (!i == x + 20);
    }())
        Console.WriteLine(<*>quot;{elem} to {x}");
    Console.ReadKey();
}
quot;{elem} to {x}") Next Console.ReadKey() End Sub

Notez également les parenthèses 'here ; la fonction lambda Fonction itératrice ... Fonction de fin renvoie un IEnumerable (Of Integer) mais est pas un tel objet lui-même. Il doit être appelé pour obtenir cet objet.

Le code converti par [1] soulève des erreurs en C # 7.3 (CS0149):

<*>

Je ne suis absolument pas d'accord avec la raison donnée dans les autres réponses qu'il est difficile pour le compilateur de gérer. La Iterator Function () que vous voyez dans l'exemple VB.NET est spécialement créée pour les itérateurs lambda.

En VB, il existe le mot clé Iterator ; il n'a pas d'équivalent C #. IMHO, il n'y a pas de vraie raison pour laquelle ce n'est pas une fonctionnalité de C #.

Donc, si vous voulez vraiment, vraiment, des fonctions d'itérateurs anonymes, utilisez actuellement Visual Basic ou (je ne l'ai pas vérifié) F #, comme indiqué dans un commentaire de Partie 7 dans la réponse de @Thomas Levesque (do Ctrl + F pour F #).

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top