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>
{
}
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, . , .
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
}