IEnumerable <T> implementation works for foreach but not LINQ

I am currently using the following base collection.

  abstract class BaseCollection : Collection<BaseRecord>
  {       
  }

I would like to replace it with the general collection below. This, however, is not a trivial task due to the number of derived classes that currently implement BaseCollection.

  abstract class BaseCollection<TRecord> : Collection<TRecord> where TRecord : BaseRecord,new()
  {

  }

Instead of a massive overhaul, I would like to gradually introduce this new collection.

   abstract class BaseCollection<TRecord> : BaseCollection,IEnumerable<TRecord> where TRecord : BaseRecord,new()
   {
      public new IEnumerator<TRecord> GetEnumerator()
      {
         return Items.Cast<TRecord>().GetEnumerator(); 
      }
   }

While I can list the collection using foreach, the LINQ statement below does not compile. Is it possible? I understand that this is a little hack, but I'm not sure how to do it.

   class Program
   {
      static void Main(string[] args)
      {
         DerivedCollection derivedCollection = new DerivedCollection
         {
            new DerivedRecord(),
            new DerivedRecord()
         };
         foreach (var record in derivedCollection)
         {
            record.DerivedProperty = "";
         }
         var records = derivedCollection.Where(d => d.DerivedProperty == "");
      }
   }

Here are the two entries used above. Thank you

   class DerivedRecord : BaseRecord
   {
      public string DerivedProperty { get; set; }
   }

   abstract class BaseRecord
   {
      public string BaseProperty { get; set; }
   }

Here is a derived collection.

   class DerivedCollection : BaseCollection<DerivedRecord>
   {

   }
+3
source share
3

Foreach "", , . , IEnumerable . , , .

, Where. , :

IEnumerable<DerivedRecord> ie = derivedCollection;
ie.Where(d => d.DerivedProperty == "");

, .

:

derivedCollection.Where<DerivedRecord>(d => d.DerivedProperty == "");

, BaseCollection IEnumerable<T>:

abstract class BaseCollection
{
    private readonly Collection<BaseRecord> _realCollection = new Collection<BaseRecord>();

    public void Add(BaseRecord rec)
    {
        _realCollection.Add(rec);
    }

    public IEnumerable<BaseRecord> Items
    {
        get { return _realCollection; }
    }
}

Collection<T>, . , API .

IEnumerable<T>.

class BaseCollection<TRecord> : BaseCollection, IEnumerable<TRecord> 
                                    where TRecord :  BaseRecord,new()
{
    public IEnumerator<TRecord> GetEnumerator()
    {
        return Items.Cast<TRecord>().GetEnumerator(); 
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

, , IEnumerable<T>.

,

, IEnumerable, , , .

public class R1 { }
public class R2 { }
public interface I<T> { }
public class C1<T> : I<T> { }
public class C2 : C1<R1>, I<R2> { }

class Program
{
    public static I<T> M<T>(I<T> i) { return i; }

    static void Main(string[] args)
    {
        var c2 = new C2();
        var v = M(c2); // Compiler error - no definition for M
    }
}

R1 R2 . R2 R1 - , . I<T> - , IEnumerable<T>. ! .

. C1 I<T>, C2 C2 I<R1>, I<R2>. . Where, .

, C2 I<T>: I<R1> I<R2>. C1, .

, M, Where Linq. , , .

, M, , T. , , , I<T>. , -, I<R1> I<R2>, ? .

, new , , . , , M I<R1> I<R2>.

? # 3.0 - , , , . , , , , - M.

( Resharper, , , IDE, : " M ". )

, foreach ? ! , . . GetEnumerator , . :

public class C
{
    public IEnumerator GetEnumerator() { return null; }
}

! (, , IEnumerator.) IEnumerable . , foreach .

, , "" BaseRecord DerivedRecord, Cast Linq. , foreach . C object. :

foreach (string item in new C())
{
    Console.WriteLine(item.Length);
}

object string. ... Yuck!

var - var foreach, . , .

+4

, :

(IEnumerable<DerivedRecord>)derivedCollection).Where(d => d.DerivedProperty == "")
0

The operation probably already solved this, but if it helps someone else, your existing Linq breaks when you try to use BaseCollection, because it has two types of IEnumerable: IEnumerable<TRecord>and IEnumerable<BaseRecord>(inherited from BaseCollection → Collection<BaseRecord>). To get the existing Linq for compilation by having two types of IEnumerable, determine which IEnumerable you want to use Linq by doing IQueryable<TypeYouWantToUse>.

abstract class BaseCollection<TRecord> : BaseCollection, IEnumerable<TRecord>, IQueryable<TRecord> where TRecord : BaseRecord, new()
{
    public new IEnumerator<TRecord> GetEnumerator()
    {
        return Items.Cast<TRecord>().GetEnumerator();
    }

    #region IQueryable<TRecord> Implementation
    public Type ElementType
    {
        get { return typeof(TRecord); }
    }

    public System.Linq.Expressions.Expression Expression
    {
        get { return this.ToList<TRecord>().AsQueryable().Expression; }
    }

    public IQueryProvider Provider
    {
        get { return this.ToList<TRecord>().AsQueryable().Provider; }
    }
    #endregion
}
0
source

Source: https://habr.com/ru/post/1729927/


All Articles