95 votes

En C #, pourquoi une méthode anonyme ne peut-elle pas contenir une déclaration de rendement?

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 disent simplement que ce n'est pas permis.

Comme cela n'était pas autorisé, j'ai simplement créé List et ajouté les éléments.

120voto

Thomas Levesque Points 141081

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

EDIT2:

  • La partie 7 (celui-ci a été publié plus tard et de la traite spécifiquement de cette question)

Vous trouverez probablement la réponse il y a...


EDIT1: c'est expliqué dans les commentaires de la Partie 5, dans Eric en réponse à Abhijeet Patel commentaire:

Q :

Eric,

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

Un :

Bonne question. J'aimerais avoir anonyme itérateur blocs. Il serait totalement génial d'être en mesure de construire une petite séquence de générateur en place et qui a clôturé au-dessus locales les variables. La raison pourquoi ne pas simple: les bénéfices ne pas l'emportent sur les coûts. L'awesomeness de prise de séquence de générateurs en place est en fait assez petite dans le grand schéma des choses nominal et méthodes faire le travail assez bien dans la plupart des les scénarios. Les bénéfices sont donc pas que convaincante.

Les coûts sont de grande taille. Itérateur la réécriture est le plus compliqué la transformation dans le compilateur, et anonyme méthode de réécriture est l' deuxième plus compliqué. Anonyme les méthodes peuvent être à l'intérieur d'autres anonymes les méthodes et les méthodes anonymes peuvent être à l'intérieur de itérateur blocs. Par conséquent, ce que nous faisons est d'abord en nous réécrire toutes les les méthodes anonymes, afin qu'ils deviennent les méthodes d'une classe de fermeture. C'est la seconde-la dernière chose que le compilateur avant émettant IL d'une méthode. Une fois que cette étape est terminée, l'itérateur graveur peut supposer qu'il n'y a pas les méthodes anonymes dans l'itérateur bloc; ils ont tous être réécrite déjà. Par conséquent, l'itérateur graveur peut juste se concentrer sur la la réécriture de l'itérateur, sans inquiétant qu'il pourrait y avoir un latente de la méthode anonyme.

Aussi, iterator blocs jamais "nid", contrairement aux méthodes anonymes. L'itérateur graveur peut supposer que tous les itérateur les blocs sont "top niveau".

Si les méthodes anonymes sont autorisés à contenir itérateur blocs, puis les deux ces hypothèses sortir de la fenêtre. Vous pouvez avoir un itérateur bloc contient une méthode anonyme qui contient une méthode anonyme qui contient un itérateur bloc contient une méthode anonyme, et... beurk. Maintenant, nous devons écrire une réécriture laissez-passer qui peut gérer imbriquée itérateur blocs et imbriqués les méthodes anonymes à le même temps, la fusion de nos deux plus des algorithmes complexes dans l'un des algorithme plus complexe. Il serait être vraiment dur à concevoir, mettre en œuvre, et de test. Nous sommes assez intelligents pour le faire donc, j'en suis sûr. Nous avons une puce de l'équipe ici. Mais nous ne voulons pas prendre sur que grand fardeau pour un "nice to have mais pas nécessaire". -- Eric

24voto

ShuggyCoUk Points 24204

Eric Lippert a écrit une excellente série d'articles sur les limites (et les décisions de conception d'influencer les choix) sur itérateur blocs

En particulier itérateur blocs sont mis en œuvre par certains sophistiqué compilateur de code transformations. Ces transformations auraient un impact avec les transformations qui se produisent à l'intérieur des fonctions anonymes ou des lambdas telle que, dans certaines circonstances, ils auraient tous les deux essayer de "convertir" le code dans une autre construction qui était incompatible avec les autres.

En conséquence, ils sont interdits à partir de l'interaction.

Comment itérateur blocs de travail sous le capot est traitée avec bien ici.

Un exemple simple d'une 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 est à la fois vouloir convertir ce à 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 dans le même temps, l'itérateur aspect est d'essayer de faire c'est de travailler pour faire un peu l'état de la machine. Certains exemples simples pourrait fonctionner avec une bonne quantité de vérifications (premier traitant de l' (éventuellement de manière arbitraire nexted fermetures), puis de voir si le très bas niveau des classes obtenues pourraient être transformés en itérateur de l'état des machines.

Mais ce serait

  1. Beaucoup de travail.
  2. Ne pouvait pas fonctionner dans tous les cas, sans à tout le moins l'itérateur bloc aspect d'être en mesure d'empêcher la fermeture de l'aspect de l'application de certaines transformations de l'efficacité (comme la promotion des variables locales variables d'instance plutôt que d'une véritable classe de fermeture).
    • Si il y avait encore une petite chance de chevauchement où il est impossible ou suffisamment dur pour ne pas être mis en œuvre alors que le nombre de questions de soutien résultant serait susceptible d'être élevé depuis la subtile modification de rupture serait perdu de nombreux utilisateurs.
  3. Il peut être très facilement contourné.

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

7voto

Lasse V. Karlsen Points 148037

Malheureusement, je ne sais pas pourquoi ils n'ont pas permettre cela, car bien entendu il est tout à fait possible de le faire imaginer comment cela pourrait fonctionner.

Cependant, les méthodes anonymes sont déjà un morceau de "compilateur de la magie", dans le sens que la méthode sera extrait soit à une méthode dans la classe existante, ou même d'une nouvelle classe, selon que l'on traite avec des variables locales ou non.

En outre, iterator méthodes à l'aide de yield est également mis en œuvre à l'aide du compilateur de la magie.

Ma conjecture est que l'une de ces deux rend le code des nations unies identifiables à l'autre morceau de la magie, et qu'il a été décidé de ne pas passer du temps à faire ce travail pour la version actuelle du compilateur C#. Bien sûr, il pourrait ne pas être un choix conscient, et qu'il ne fonctionne tout simplement pas, parce que personne n'a pensé à le mettre en œuvre.

Pour un 100% précises sur la question, je vous suggère d'utiliser le Microsoft Connect site et rapport une question, je suis sûr que vous aurez quelque chose d'utilisable en retour.

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