Étant donné une énumérable, je veux prendre () tous les éléments jusqu'à et inclure un terminateur (jetant une exception si le terminateur n'est pas trouvé). Quelque chose comme: .. Sauf pas de merdique. Seulement envie de le marcher une fois. P> est-ce possible avec des opérateurs actuels dans .NET 4 et RX ou dois-je écrire un nouvel opérateur? P> écrire l'opérateur me prendrait moins de temps qu'il ne l'a fait pour écrire cette question (bien que je pense que la moitié de cette période déterminerait quoi nommer cette fonction), mais je ne veux tout simplement pas dupliquer quelque chose qui est déjà là. p> OK, voici l'opérateur. Très excitant, je sais. Quoi qu'il en soit, possible de le construire à partir d'opérateurs intégrés? P>
3 Réponses :
Voici quelques hardcore si vous ne voulez pas écrire votre propre opérateur:
C'est vraiment cool dans un "J'espère ne jamais courir dans cela au travail".
Je l'aime aussi, ce n'est pas souvent que l'on s'efforce d'abuser de la déclaration enfin. Mais, comme ma solution, il lancera toujours une exception si vous n'exécutez pas l'énumérable avant le chèque.
Malheureusement, nous ne pouvons pas utiliser MainMentiment () Code> Dans nos solutions, car il se termine trop tôt. Nous devons vérifier que toute la séquence contient un seul élément défaillant (en passant par la logique de la requête d'origine).
À mon avis, tout va d'abord, est la seule solution sûre. Vous ne pouviez pas faire de véritable travail avec des articles aussi éloignés, si les énumérateurs pourraient organiser une exception à la fin.
@skarmats, merci d'avoir mentionné que, résolue le code pour éviter une exception causée par la paresse Linq
Il n'y a pas de construction pour faire une telle opération efficacement. Ce n'est pas très souvent que les gens auraient besoin d'obtenir des objets qui satisfont à une condition et que cela ne le fait pas. Vous devriez l'écrire vous-même.
Cependant, vous pouvez construire cela à l'aide de méthodes existantes, cela ne sera tout simplement pas aussi efficace que vous auriez besoin de garder l'état en quelque sorte compliquer votre code. Je ne tolérais pas ce genre de requête alors qu'il va à l'encontre de la philosophie de Linq et de l'écrire moi-même. Mais depuis que vous avez demandé: p> Cependant, cela a ses propres problèmes qui ne peuvent pas vraiment être résolus bien. La bonne façon de faire face à cela est de coder tout cela à la main (sans linq). Cela vous donnera exactement le même comportement. P> public static IEnumerable<TSource> TakeWhileSingleTerminated<TSource>(
this IEnumerable<TSource> source,
Func<TSource, bool> predicate)
{
var hasTerminator = false;
var terminator = default(TSource);
foreach (var item in source)
{
if (!hasFailed)
{
if (predicate(item))
yield return item;
else
{
hasTerminator = true;
terminator = item;
}
}
else if (!predicate(item))
throw new InvalidOperationException("Sequence contains more than one terminator");
}
if (!hasTerminator)
throw new InvalidOperationException("Sequence is not terminated");
yield return terminator;
}
Cela rend difficile de jeter une exception si le terminateur n'a pas été trouvé cependant. Ne pense pas que la liste est vraiment toujours une liste. Voir ma réponse
@skarmats: le type actuel de la "liste" est hors de propos, aussi longtemps que c'est un ienumerable
@Jeff m: Mon marmonnier sur la liste est né de la pensée que l'on pouvait faire une comparaison sur .Compte à voir si nous devons jeter une exception. Le long des lignes de "n'a pas trouvé de terminateur" && "Count est égal à la source". Ce que je n'aime pas à propos de la solution de l'opérateur, celui-ci énumère beaucoup d'articles, puis éventuellement les jette à l'exception d'exception (toutes nos solutions le font aussi bien sûr ...). Voir ma question sur la question postale.
@skarmats: Tu m'as fait voir la lumière. Au début, je pensais que cela pouvait être fait relativement facilement, mais maintenant que vous ayez soulevé (à juste titre) les autres conditions, je suis arrivé à la conclusion que ce problème n'aura qu'une belle période de solution.
@Jeff m: Il est presque comme s'il s'agissait d'une décision délibérée des concepteurs-cadres, que toutes les méthodes pouvant lancer une exception dans un tel scénario ne peuvent également renvoyer à 1 élément de la source. Il n'y a pas de problème associé à cela. Bien de toute façon. Merci pour le bilan et en avant, je n'aurais pas donné beaucoup de pensée à cela seul.
Votre dernière version de cet article a un comportement nettement différent de la requête d'origine cependant. Cela comme ça, ça échouera tard. L'original échouera rapidement. Le .single () dans la requête d'origine est exécuté en premier - avant que toute énumération ne commence.
@skarmats: La requête originale que je faisais appelé était celle composée de la combinaison de à l'épreuve du code () code> et unique () code>. Cela ne fonctionne pas réellement car il a été écrit mais je pense que l'intention était claire. Il va énumérer à travers les articles qui satisfont le Main-là () code>, puis tentez de prendre la seule condition satisfaisante éventuellement défaillante alors. Malheureusement, concat () code> ajoute des séquences, pas des éléments individuels, donc cela échouerait à la fin depuis la fin de la seconde qui est énumérée après le premier. Je vais laisser cela à cela, j'ai passé assez de temps à ce sujet. :) (et maintenant je viens de comprendre que j'ai les conditions inversées)
@Jeff m: Oui, la requête originale ne compile pas. Et oui, si vous l'écrivez comme .concat (liste.LOù (Cond) .take (1)) Vous avez raison - il sera paresseusement évalué. Mais afin de rester près de la pseudo-requête d'origine, j'ai utilisé .single () aussi proche que possible de la déclaration originale. Fondamentalement via le nouveau t []. Si vous ne cachez pas la cassette () dans un rendement de rendement, il sera toujours évalué immédiatement.
@Jeff m: Je sais ce que vous voulez dire ce que vous voulez dire - ce n'était probablement pas ce que l'auteur avait à l'esprit ... Mais ce que je dis est, l'une des premières / une classe unique de méthodes agit comme Tolist en ce sens qu'ils sont immédiatement évalués Si non caché artificiellement.
int? j = null;
var result = list.TakeWhile((o, i) =>
{
if (j == null && cond(o)) { j = i + 1; }
return (j ?? -1) != i;
});
if (j == null) { throw new InvalidOperationException(); }
I'd go for operator, but how can one ever be sure there really isn't a built-in way? ;-)UPDATE1: Ok, my code is useless. I'd bet it always throws an exception, if execution of the Enumerable has not taken place before the check for the exception...
Je voudrais écrire un opérateur pour faire le travail ...
Je serais d'accord - vous pourriez probablement utiliser un clignotant
zip () code> pour un look-avant, mais cela traverserait toujours l'énumération deux fois - l'opérateur est la voie à suivre.BTW, quelle est votre prise sur le fait que de nombreux articles sont cédés avant même que nous sachions même si nous sommes toujours autorisés à utiliser ces articles (c'est-à-dire une exception n'est pas lancée)?
Eh bien, je suppose que c'est toujours le meilleur que l'on puisse faire dans la plupart des cas, même avec des énumérables en streaming. Encore une fois, s'il est en streaming, vous ne pouvez pas vraiment travailler sur les articles jusqu'à ce qu'il n'y ait eu aucune confirmation sur une sélection réussie. Un peu peu satisfaisant. Une solution complètement sûre devrait toujours vérifier le succès avant de les donner. (Streaming dans le sens de "récupération coûteuse de certaines sources")