【问题标题】:Why might you use IEnumerable as a method return type or in a constructor [closed]为什么可以使用 IEnumerable 作为方法返回类型或在构造函数中[关闭]
【发布时间】:2016-04-30 23:13:05
【问题描述】:

我是 C# 的初级程序员,我一直在做一个使用 IEnumerable 的练习。

我想我理解代码,但我很难理解为什么你会以这种方式使用它

示例 1

考虑以下孤立的代码.....

private List<Card> cards;

public Deck (IEnumerable<Card> initialCards)
{
    cards = new List<Card>(initialCards);
}

创建一个deck对象并调用这个构造函数的代码是……

Card[] cardArray = new Card[numberOfCards];
for (int i = 0; i < numberOfCards; i++)
{
    cardArray[i] = new Card((Suits)random.Next(0, 4), (Values)random.Next(0, 12));
}
deck1 = new Deck(cardArray);

如果有人不介意在下面确认我的理解......

  • 我们创建一个 Card 类型的数组并用 Cards 填充它
  • 在创建 Deck 对象时,我们将 Card [] 数组传递给 Deck 构造函数。即使构造函数参数包含一个 IEnumerable 类型的参数,它也被认为是有效的,因为数组实现了 IEnumerable。 这里有演员表吗?
  • 列表卡片使用传入重载列表构造函数的 IEnumerable 参数进行初始化。 再一次,这是一个演员表还是某种类型的转换?

使用 IEnumerable 作为中间类型似乎完全没有意义。代码仍然可以正常工作,包括如果我将构造函数更改为最终使用的 foreach 循环...

public Deck (Card [] initialCards)
{
    cards = new List<Card>(initialCards);
}

示例 2

public IEnumerable<string> GetCardNames()
{
    string[] stringArray = new string[cards.Count];
    for (int i = 0; i < stringArray.Length; i++)
    {
        stringArray[i] = cards[i].ToString();
    }
    return stringArray;
}

这是来自同一类的方法。同样,当我们仍然可以对字符串数组执行 foreach 时,我不确定为什么它会作为 IEnumerable 返回。

示例 3

public IEnumerable<IWebElement> ElementCollection { get; set; }

这很愚蠢,但有人可以向我解释这是在说什么吗?它是 IEnumerable 类型的公共属性吗?当您将接口用作属性类型时,它与其他类型有何不同?例如字符串

这在后面的代码中与 LINQ 一起使用,我在学习中还没有用到。这个例子是否表明如果我们只想迭代某些东西,可以使用 IEnumerable?

非常感谢,

【问题讨论】:

  • 可枚举是对某些序列操作的概括——这些操作包括数组提供的操作的子集。所有数组(或列表)实现 IEnumerable.

标签: c# interface ienumerable


【解决方案1】:

返回接口(在本例中为IEnumerable)或接受它们是面向对象编程中的一种核心方法。这混合了 OOP 的三个核心原则,尤其是抽象

在长期支持申请中,您经常会做出更改。在您的应用程序开始时,您确实需要固定大小的Card 集合,因此您确实使用了一个数组。

private Card[] cards;

public Deck (Card[] initialCards)
{
    cards = initialCards;
}

在某些时候,您需要继续使用非固定大小的 Card 集合。所以你搬到List

private List<Card> cards;

public Deck (Card [] initialCards)
{
    cards = new List<Card>(initialCards);
}

在您的应用程序的一部分中,您也有 ListCard。你想将它传递给你的构造函数。你不能!即使你的内部结构是List,你的构造函数仍然接受一个数组(Card[])。

您需要从这个外部部分将您的List 转换为一个数组。为此,您需要遍历所有列表并在内存中创建一个新结构 - 一个卡片数组。如果你的牌组有数百万张牌,那可能会很贵。

所以你改变你的构造函数来接受List

private List<Card> cards;

public Deck (List<Card> initialCards)
{
    cards = initialCards;
}

在某些时候,您的应用程序会增长。您需要使用独特的卡片玩游戏。您的应用程序的一部分创建了HashSet 的卡片。您不能将其传递给构造函数。该死的构造函数正在接受List's。如果您将构造函数更改为接受HashSet,您将破坏应用程序中传递List 的另一部分的逻辑。

您希望应用程序的两个部分以相同的方式传递HashSetList,而您的套牌只是获取此集合并将其分配给内部结构。

您真正需要的是收集Cards 的集合,该集合可以是foreach'd 并在以后枚举它以供其他用途。然后你只需告诉应用程序的其他部分——“伙计们,给我一个可以枚举的对象。我真的不在乎它是SetList 还是Array”。

private IEnumerable<Card> cards;

public Deck (IEnumerable<Card> initialCards)
{
    cards = initialCards;
}

另一种方式也是如此。它降低了复杂性。您只需告诉您的消费者,您将给他们一个 Card 名称的集合以进行枚举。他们需要列出它并查看一些卡片名称。他们不需要更改名称、添加新卡或其他任何事情。

您不会公开您的实施细节。您只需在对其内部结构了解最少的情况下就可以使用您的对象。作为封装的一部分,您可能会发现这种隐藏信息的方式。

【讨论】:

  • 精彩的解释。谢谢:)
【解决方案2】:

示例 1

我们创建一个 Card 类型的数组并用 Cards 填充它

不是问题,而是正确的。

我们在创建一个卡片时将 Card [] 数组传递给 Deck 构造函数 甲板对象。即使构造函数参数包含参数 IEnumerable 类型的,它被认为是有效的数组实现 可数的。这里有演员表吗?

这是一种隐式类型的演员表。当类型实现接口时,您不需要向下转换,因为该类型是“该类型的”。例如,您不会要求谈论水果 Kiwi 的每个人都区分“猕猴桃”和“水果”的意思,因为猕猴桃具有水果的所有特性。

列表卡片是用传入的 IEnumerable 参数初始化的 到重载的列表构造函数。再次,这是一个演员还是一个 某种转换?

如果您检查,List 包含一个 IEnumerable 构造函数。当您的构造函数传递了一个 IEnumerable 时,无需强制转换即可将一个 IEnumerable 转换为另一个 IEnumerable。

使用 IEnumerable 作为中间类型似乎完全没有意义。代码仍然可以正常工作,包括如果我将构造函数更改为最终使用的 foreach 循环...

public Deck (Card [] initialCards)
{
    cards = new List<Card>(initialCards);
}

这不是没有意义的,将构造函数更改为 this 会破坏任何试图将列表发送到构造函数的代码。

示例 2

public IEnumerable<string> GetCardNames()
{
    string[] stringArray = new string[cards.Count];
    for (int i = 0; i < stringArray.Length; i++)
    {
        stringArray[i] = cards[i].ToString();
    }
    return stringArray;
}

这是来自同一类的方法。同样,我不确定这是为什么 当我们仍然可以对字符串执行 foreach 时以 IEnumerable 的形式返回 数组。

通常您会将这样的方法放在接口中,并保证每个实现都可以返回 IEnumerable。通常使用 IEnumerables,您可以使用 yield 和巧妙的逻辑来推迟计算,同时调用 GetCardNames() 因为 Array 会直接强制执行计算。使用 IEnumerable 的原因有很多,但它们通常指向某种“保证”,您无法保证数组符合。

示例 3

public IEnumerable<IWebElement> ElementCollection { get; set; }

这很菜,但有人可以向我解释这是什么 说?它是 IEnumerable 类型的公共属性吗?当您使用 接口作为属性类型与其他类型有何不同? 例如字符串

这在后面的代码中与 LINQ 一起使用,我没有在我的 学习呢。这个例子可能表明,如果我们只希望 迭代一些东西,可以用IEnumerable吗?

正如您所说,它是一个公共 IEnumerable 属性,具有默认支持者等。IEnumerable 保证它可以被迭代,这是一个非常强大的保证,因为它允许搜索和更多的东西。仅仅因为您可以迭代它并不意味着这些项目是连续的或其他任何东西,因此实现可能会有所不同。这是接口的优势之一。

您的代码中的不同位置也可以为 IEnumerable 属性分配不同的类型。有些人可能会向它发送列表,其他人可能会向它发送数组。还可以将计算推迟到需要发生的时候。

【讨论】:

    【解决方案3】:

    其他的回答都非常透彻。这是一个略短的:

    数组、列表和其他集合类型都实现了IEnumerable。将方法参数声明为 IEnumerable 表示该方法并不“关心”实际类型,只要它实现了 IEnumerable

    如果你的方法是通过一个集合去for...each,那为什么指定它需要是一个数组或者一个列表呢?当您可以不那么具体时,无需更具体。

    这也是一种告诉方法期望对传递给它的参数做什么的方式。如果一个方法采用IEnumerable&lt;T&gt; 的参数,它实际上是在说,“我只是要获取你的集合并枚举它。我不会添加或删除项目。”

    如果参数是List&lt;T&gt;IList&lt;T&gt;,那么接收它的方法可以添加或删除项目。

    不太常见,为了便于说明 - 如果将数组传递给方法,该方法可能会更改数组的排序顺序。 (老实说,这不是我通常关心的可能性。)

    当然,指定这些类型并不能绝对强制执行这些行为,但它是方法传达其行为的一种方式。您添加到列表中。你枚举一个IEnumerable

    【讨论】:

      猜你喜欢
      • 2013-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-08
      • 2021-01-25
      • 1970-01-01
      • 2015-06-05
      相关资源
      最近更新 更多