C# IEnumerable的实现<;T>;为foreach工作,但不是LINQ
我目前使用以下基本集合C# IEnumerable的实现<;T>;为foreach工作,但不是LINQ,c#,generics,ienumerable,C#,Generics,Ienumerable,我目前使用以下基本集合 abstract class BaseCollection : Collection<BaseRecord> { } abstract class BaseCollection<TRecord> : Collection<TRecord> where TRecord : BaseRecord,new() { } abstract class BaseCollection<TReco
abstract class BaseCollection : Collection<BaseRecord>
{
}
abstract class BaseCollection<TRecord> : Collection<TRecord> where TRecord : BaseRecord,new()
{
}
abstract class BaseCollection<TRecord> : BaseCollection,IEnumerable<TRecord> where TRecord : BaseRecord,new()
{
public new IEnumerator<TRecord> GetEnumerator()
{
return Items.Cast<TRecord>().GetEnumerator();
}
}
这是上面使用的两条记录。谢谢
class DerivedRecord : BaseRecord
{
public string DerivedProperty { get; set; }
}
abstract class BaseRecord
{
public string BaseProperty { get; set; }
}
这是派生的集合
class DerivedCollection : BaseCollection<DerivedRecord>
{
}
class-DerivedCollection:BaseCollection
{
}
甚至更难看,但可以:
(IEnumerable<DerivedRecord>)derivedCollection).Where(d => d.DerivedProperty == "")
(IEnumerable)derivedCollection)。其中(d=>d.DerivedProperty==“”)
Foreach使用一些“魔法”来确定要循环的类型。它不需要集合支持任何内容的IEnumerable
。这可能解释了这种差异
问题是编译器无法找出Where
的泛型类型参数。如果您这样做,它将工作:
IEnumerable<DerivedRecord> ie = derivedCollection;
ie.Where(d => d.DerivedProperty == "");
在这里,我仍然在实例化集合
,但是作为一个单独的对象而不是基类。然后根据需要将内容转发给它以模拟其API
派生的泛型版本需要完全实现IEnumerable
R1
和R2
类似于两个记录类。在您的版本中,R2
继承了R1
——但这与此问题无关,所以我省略了它I
是通用接口,代表IEnumerable
。但是我的版本没有办法!它们也与这个问题无关
然后,我将集合类继承系统折叠为两层。基类C1
实现I
,然后派生类C2
选择请求C2
实现I
,然后还直接实现I
。层数也没有区别。使用where
声明的类型约束也不存在,因此这里也省略了它们
结果是C2
有两种I
的实现:I
和I
。一个是从C1
继承的,另一个是自己添加的
最后我得到了一个方法M
,它代表Linq中的Where
。它不需要是一个扩展方法来显示这个问题,所以为了清晰起见,我将它作为一个普通的静态方法
因此,当我们调用方法M
时,编译器必须弄清楚T
是什么。它通过查看我们传递给该方法的唯一参数来实现这一点,该方法必须支持I
。不幸的是,我们正在传递支持I
和I
的内容,那么类型推断过程如何在它们之间做出选择呢?不可能
由于我的界面没有方法,显然,将new
放在方法前面对我没有帮助,这就是为什么它对你没有帮助。问题不是决定调用接口中的哪个方法,而是将M
的参数视为I
还是I
为什么编译器不将此报告为类型推断问题?根据C#3.0规范,它只是没有先运行类型推断,生成一组可用的重载,然后运行重载解析以选择最佳选择。如果类型推断无法决定泛型方法的两个可能扩展,它会消除这两个扩展,因此重载解析甚至不会看到它们,因此错误消息说没有任何可用的方法称为M
(但是如果您有Resharper,它有自己的编译器,它使用它在IDE中提供更详细的错误,在本例中,它特别指出:“方法M的类型参数不能从用法中推断出来”。)
现在,为什么foreach
不同?因为它甚至都不是类型安全的!它可以追溯到添加泛型之前。它甚至不看接口。它只是在循环通过的任何类型中查找名为GetEnumerator
的公共方法。例如:
public class C
{
public IEnumerator GetEnumerator() { return null; }
}
就编译器而言,这是一个非常好的集合!(当然,它会在运行时爆炸,因为它返回nullIEnumerator
),但请注意,没有IEnumerable
或任何泛型。这意味着foreach
执行隐式强制转换
因此,为了将其与您的代码关联起来,您有一个从BaseRecord
到DerivedRecord
的“向下转换”,您可以使用Linq中的cast
操作符实现该转换。不管怎样,foreach
都是为你做的。在我上面的示例中,C
实际上是object
类型的项的集合。但我可以写:
foreach (string item in new C())
{
Console.WriteLine(item.Length);
}
编译器愉快地插入了一个从对象
到字符串
的静默转换。那些东西可能是任何东西。。。恶心
这就是为什么
var
的出现非常好-始终使用var
声明foreach
循环变量,这样编译器就不会插入强制转换。它将使变量成为编译时从集合中推断出的最特定类型。op可能已经解决了这个问题,但是如果它对其他人有帮助,那么当您尝试使用BaseCollection时,您现有的Linq将中断,因为它有两种IEnumerable类型:IEnumerable
和IEnumerable
(继承自BaseCollection->Collection
)。若要在拥有两个IEnumerable类型的情况下编译现有Linq,请通过实现IQueryable
定义希望Linq使用的IEnumerable
抽象类BaseCollection:BaseCollection,IEnumerable,IQueryable其中TRecord:BaseRecord,new()
{
公共新IEnumerator GetEnumerator()
{
返回Items.Cast().GetEnumerator();
}
#区域可扩展实现
公共类型ElementType
{
获取{return typeof(TRecord);}
}
public System.Linq.Expressions.Expression表达式
{
获取{返回this.ToList().AsQueryable().Expression;}
}
公共IQueryProvider提供程序
{
获取{返回this.ToList().AsQueryable().Provider;}
}
#恩德
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; }
}
}
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();
}
}
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
}
}
public class C
{
public IEnumerator GetEnumerator() { return null; }
}
foreach (string item in new C())
{
Console.WriteLine(item.Length);
}
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
}