c#中IEnumerable接口的一些说明
本文关键字:说明 接口 IEnumerable | 更新日期: 2023-09-27 17:49:41
我对 c# 很陌生(我来自Java),我对使用:
有疑问我有这样一个类:
namespace DataModel.MaliciousCode
{
public class PagedMalicious : Shared.Paged
{
public IEnumerable<MaliciousSmall> MaliciousCode { get; set; }
}
}
你可以看到这个类只包含一个IEnumerable<MaliciousSmall> MaliciousCode
阅读在线文档,我似乎理解了IEnumerable
是一个接口,它给了我一个非泛型集合的迭代器。
前面的断言究竟是什么意思?
我有MaliciousSmall,这是我的应用程序中的模型对象的类型(一个对象,包含一些属性,映射数据库上表的字段)
所以我的疑问是:
IEnumerable<MaliciousSmall> MaliciousCode
: MaliciousCode是MaliciousSmall对象的可迭代集合?所以这意味着它代表一个集合和一些提供的方法来迭代它?如果前面的断言是正确的,ok MaliciousCode object是一个可迭代的集合,但IEnumerable是一个接口,所以谁实现这个集合上迭代的方法(来自Java,我认为接口没有提供实现的方法)
Tnx
Andrea
IEnumerable<MaliciousSmall> MaliciousCode
:MaliciousCode
是MaliciousSmall
对象的可迭代集合?所以这意味着它代表一个集合和一些提供的方法来迭代它?
Sort of - IEnumerable<T>
提供了一个方法- GetEnumerator
-它返回一个IEnumerator<T>
。接口允许您遍历集合。在linq之前,所有IEnumerable
允许您在foreach
循环中使用集合(或直接使用提供的IEnumerator
,这很少见)。Linq已经在IEumerable<T>
上定义了扩展方法,允许更复杂的查询,如Select
, Where
, Count
等。
如果前面的断言是正确的,那么
MaliciousCode
对象是一个可迭代的集合,但是IEnumerable
是一个接口,所以谁实现了迭代这个集合的方法(来自Java,我认为接口没有提供实现的方法)
通常通过使用底层集合类型(如List<MaliciousSmall>
或MaliciousSmall[]
)提供实现。IEnumerable实现是由那个类提供的。在c# 2.0中引入的yield
关键字允许你"返回"一个IEnumerable<T>
,并让编译器提供实际的实现。
List<T>
:
public class PagedMalicious : Shared.Paged
{
public IEnumerable<MaliciousSmall> MaliciousCode { get; set; }
public PagedMalicious()
{
MaliciousCode = new List<MaliciousSmall>();
}
// other private methods that add to MaliciousCode
}
使用IEnumerable<T>
允许您在不更改公共接口的情况下更改内部实现。
您的属性MaliciousCode
,表示实现IEnumerable<T>
接口的类的对象。就其本身而言,IEnumerable
并不意味着什么。它只是提供了一个结构。用户有责任以他们认为合适的方式实现接口提供的方法。
编辑:这里有一个简单的例子来演示:
private void Form3_Load(object sender, EventArgs e)
{
Parent parent = new Parent();
parent.Child = new List<Child>(); // -> this is where implementer is decided.
//Before this line, Child property is not instantiated and is not referring to any object.
}
public class Parent
{
public IEnumerable<Child> Child { get; set; }
}
public class Child
{
public int MyProperty { get; set; }
}
您的疑问:是的,是的接口本身不能实现任何东西,但是您可以分配一个MaliciousSmall数组或listmalicioussmall>。它们都实现了IEnumerable
IEnumerable<T>
相当于Java的Iterable<T>
。由于早期版本的c#没有泛型,IEnumerable
是当时唯一可用的迭代器。你可以把它想象成一种IEnumerable<object>
。
大多数泛型集合类型实现IEnumerable<T>
,包括数组。泛型变体需要实现非泛型变体,因此大多数集合(泛型或非泛型)实现IEnumerable
。
但是,这些迭代器并不局限于表示集合。它们提供了允许枚举项目的方法,这些方法也可以通过算法生成项目。例如,从理论上讲,枚举可以提供平方数的无限枚举,而不需要将这些数存储在任何地方。
在您的例子中,IEnumerable<MaliciousSmall> MaliciousCode
属性可以从DB中一个接一个地产生MaliciousSmall
对象,因为枚举正在被枚举,而不是首先将它们存储在集合对象中。
自己实现IEnumerable<T>
需要实现IEnumerator<T> GetEnumerator()
方法。它返回一个枚举对象,该对象需要实现方法bool MoveNext()
、void Dispose()
、void Reset()
和属性T Current { get; }
。
您可以通过编写大量代码来实现这些接口,或者您可以使用c#的迭代器。迭代器使用许多编译器的魔法在后台自动创建枚举对象和枚举数。参见:迭代器(c#和Visual Basic)。
作为c#迭代器的一个例子,让我们用它们来实现你的例子(我省略了setter,因为它在这里碍事):
public class PagedMalicious : Shared.Paged
{
public IEnumerable<MaliciousSmall> MaliciousCode
{
get
{
using (var conn = new SqlConnection("<my server connection>")) {
var cmd = new SqlCommand("SELECT name, number FROM myTable", conn);
conn.Open();
using (var reader = cmd.ExecuteReader()) {
while (reader.Read()) {
var maliciousSmall = new MaliciousSmall {
Name = reader.GetString(0),
Number = reader.GetInt32(1)
};
yield return maliciousSmall;
}
}
}
}
}
}
每次执行yield return
时,控制被传递回调用者,调用者获得下一项。getter方法的状态保持不变,它的执行在这里暂停,直到调用者继续迭代并需要下一项。当他这样做时,在yield return
语句之后恢复执行。
从这个例子中可以看出,枚举是以惰性方式求值的。下面的代码汇总了整个表的数字。这些项从不存储在集合中;它们从DB中检索并在枚举时创建。如果您有一百万条记录,这是一个优势!(您将在一段高效代码中使用SQL SUM
聚合函数,因此)
var pagedMalicious = new PagedMalicious();
int sum = 0;
foreach (MaliciousSmall item in pagedMalicious.MaliciousCode) {
sum += item.Number;
}