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

c#中IEnumerable接口的一些说明

IEnumerable<MaliciousSmall> MaliciousCode: MaliciousCodeMaliciousSmall对象的可迭代集合?所以这意味着它代表一个集合和一些提供的方法来迭代它?

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;
}