Using LINQ for an object that only has GetEnumerator()

后端 未结 3 1355
萌比男神i
萌比男神i 2021-02-06 22:33

Can you use LINQ in an object that exposes only Add(), Remove(), Count(), Item() and GetEnumerator() from System.Collections.IEnumerator?

相关标签:
3条回答
  • 2021-02-06 22:54

    The existing Linq extension methods work on objects that implement IEnumerable<T>. I assume your object implements the non-generic IEnumerable interface. In that case you can use the Cast<T> extension method to get a generic IEnumerable<T> wrapper. For instance, if the elements are of type int :

    var wrapper = myObject.Cast<int>();
    

    You can now use Linq on the wrapper

    0 讨论(0)
  • 2021-02-06 23:06

    No you can't. All LINQ methods are extensions methods for the IEnumerable<T> interface.

    So you'll have to implement IEnumerable<T> to use LINQ with your own collections.

    0 讨论(0)
  • 2021-02-06 23:11

    Not directly. You could fairly easily write a SingleShotEnumerable<T> though:

     public sealed class SingleShotEnumerable<T> : IEnumerable<T>
     {
         private IEnumerator<T> enumerator;
    
         public SingleShotEnumerable(IEnumerator<T> enumerator)
         {
             if (enumerator == null)
             {
                 throw new ArgumentNullException("enumerator");
             }
             this.enumerator = enumerator;
         }
    
         public IEnumerator<T> GetEnumerator()
         {
             if (enumerator == null)
             {
                 throw new InvalidOperationException
                     ("GetEnumerator can only be called once");
             }
             var ret = enumerator;
             enumerator = null;
             return ret;
         }
    
         IEnumerator IEnumerable.GetEnumerator()
         {
             return GetEnumerator();
         }
     }
    

    This is assuming you actually have an IEnumerator<T>. If you just have IEnumerator, you could write something similar only implementing IEnumerable, then use Cast or OfType to get to an IEnumerable<T>.

    (Note: this isn't thread-safe. You could make it so with locks if you really wanted.)

    You could then do:

     var filtered = from person in new SingleShotEnumerable<Person>(personEnumerator)
                    where person.Age > 18
                    select person.Name;
    

    ... but you couldn't use the query twice.

    How have you got into the strange situation of only having an IEnumerator<T> anyway? That's quite rare. See if you can design your way around that to avoid having to do something like the above, which is quite fragile.

    (An alternative would be to "drain" the IEnumerator<T> to a List<T> or something similar, but that has problems dealing with large or potentially infinite sequences.)

    0 讨论(0)
提交回复
热议问题