5 votes

Interface C# IEnumerable Any() sans spécifier les types génériques

J'ai lancé

var info = property.Info;
object data = info.GetValue(obj);

...

var enumerable = (IEnumerable)data;

if (enumerable.Any())  ///Does not compile
{
}

if (enumerable.GetEnumerator().Current != null) // Run time error
{
}

et j'aimerais voir si cette énumération a des éléments, en utilisant la requête Linq Any(). Mais malheureusement, même en utilisant Linq, je ne peux pas.

Comment faire sans spécifier le type générique ?

18voto

Jon Skeet Points 692016

Bien que vous ne puissiez pas le faire directement, vous pouvez le faire par l'intermédiaire de Cast :

if (enumerable.Cast<object>().Any())

Cela devrait toujours fonctionner, car tout IEnumerable peut être enveloppée comme un IEnumerable<object> . Il finira par mettre en boîte le premier élément s'il est en fait un IEnumerable<int> ou similaire, mais cela devrait fonctionner. Contrairement à la plupart des méthodes LINQ, Cast y OfType cible IEnumerable plutôt que IEnumerable<T> .

Vous pourriez écrire votre propre sous-ensemble de méthodes d'extension, comme celles de LINQ, mais en opérant sur les méthodes non génériques de IEnumerable type si vous le souhaitez, bien sûr. L'implémentation de LINQ to Objects n'est pas terriblement difficile - vous pouvez utiliser ma méthode Projet Edulinq comme point de départ, par exemple.

Dans certains cas, il est possible de mettre en œuvre Any(IEnumerable) un peu plus efficacement qu'en utilisant Cast - par exemple, en prenant un raccourci si la cible implémente la méthode non générique ICollection l'interface. À ce stade, il n'est pas nécessaire de créer un itérateur ou de prendre le premier élément. Dans la plupart des cas, cela ne fera pas une grande différence en termes de performances, mais c'est le genre de choses que vous devez faire. podría si vous optimisiez.

2voto

user2864740 Points 18869

Une méthode consiste à utiliser foreach comme indiqué dans IEnumerable "Remarques" . Il fournit également des détails sur les méthodes supplémentaires issues du résultat de GetEnumerator.

bool hasAny = false;
foreach (object i in (IEnumerable)(new int[1] /* IEnumerable of any type */)) {
    hasAny = true;
    break;
}

(qui est elle-même facilement transférable à une méthode d'extension).

1voto

Jon Hanna Points 40291

Votre tentative d'utiliser GetEnumerator().Current a essayé d'obtenir la valeur actuelle d'un énumérateur qui n'avait pas encore été déplacé en première position. Le résultat aurait également été erroné si le premier élément existait ou était nul. Ce que vous auriez pu faire (et ce que la fonction Any() en Enumerable fait) est de voir s'il est possible ou non de se rendre à ce premier élément, c'est-à-dire s'il existe un premier élément vers lequel se diriger :

internal static class UntypedLinq
{
    public static bool Any(this IEnumerable source)
    {
        if (source == null) throw new ArgumentNullException(nameof(source));
        IEnumerator ator = source.GetEnumerator();
        // Unfortunately unlike IEnumerator<T>, IEnumerator does not implement
        // IDisposable. (A design flaw fixed when IEnumerator<T> was added).
        // We need to test whether disposal is required or not.
        if (ator is IDisposable disp)
        {
            using(disp)
            {
                return ator.MoveNext();
            }
        }

        return ator.MoveNext();
    }

    // Not completely necessary. Causes any typed enumerables to be handled by the existing Any
    // in Linq via a short method that will be inlined.
    public static bool Any<T>(this IEnumerable<T> source) => Enumerable.Any(source);
}

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