为什么可以使用IEnumerable作为方法返回类型或在构造函数中

时间:2016-04-30 23:13:05

标签: c# interface ienumerable

我是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的数组并用卡填充
  • 在创建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吗?

非常感谢,

3 个答案:

答案 0 :(得分:4)

返回接口(在本例中为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);
}

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

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

因此,您将构造函数更改为接受List

private List<Card> cards;

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

您的应用程序在某些时候会增长。你需要用独特的牌来玩游戏。应用程序的一部分会创建HashSet张卡片。您无法将其传递给构造函数。该死的构造函数正在接受List。如果您将构造函数更改为接受HashSet,则会破坏通过List的应用程序的其他部分的逻辑。

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

您真正需要的是获取Cards的{​​{1}}集合,然后将其枚举以供其他用途使用。然后你只需告诉你应用程序的其他部分 - &#34;伙计们,给我一个可以枚举的对象。我真的不在乎它是foreachSet还是List&#34;。

Array

另一种方式也是如此。它降低了复杂性。您只需告诉您的消费者,您将为他们提供一组private IEnumerable<Card> cards; public Deck (IEnumerable<Card> initialCards) { cards = initialCards; } 名称以进行枚举。他们需要列出它并查看一些卡片名称。他们不需要更改姓名,添加新卡或其他任何东西。

您不会公开您的实施细节。您只需提供使用对象的机会,而对其内部结构的理解最少。这种隐藏信息的方式可以作为封装的一部分找到。

答案 1 :(得分:1)

示例1

  

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

不是问题,但是正确。

  

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

这是隐式的演员类型。当类型实现接口时,您不需要向下转换,因为类型是'该类型'。例如,你不会要求所有谈论水果猕猴桃的人在他们的意思是“Kiwi”和“Fruit”之间有所区别,因为Kiwi具有水果的所有特性。

  

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

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

  

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

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

这并非毫无意义,将构造函数更改为会破坏尝试将Lists发送到构造函数的任何代码。

示例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,您可以在调用GetCardNames()时使用yield和clever逻辑推迟计算,因为Array会直接强制执行计算。使用IEnumerable有很多原因,但通常它们指向某种“保证”,你不能保证Arrays符合。

示例3

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

这是非常无声的,但有人可以向我解释这是什么   说什么?它是IEnumerable类型的公共属性吗?当你使用   接口作为属性类型与其他类型有什么不同?   例如串

     

这与代码中的LINQ一起使用,我没有在我的代码中使用它   学习了。可能这个例子暗示如果我们只希望   迭代某事,可以使用IEnumerable吗?

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

您的代码中的不同位置也可以为IEnumerable属性指定不同的类型。有些人可能会发送列表,有些人可能会发送列表。还要将计算推迟到需要发生的时间。

答案 2 :(得分:1)

其他答案非常彻底。这是一个稍短的一个:

数组,列表和其他集合类型都实现IEnumerable。将方法参数声明为IEnumerable表示该方法不关心&#34;实际类型是什么,只要它实现IEnumerable

如果您的方法通过集合转到for...each,那么为什么要指定它需要是数组还是列表?当你不那么具体时,没有必要更具体。

它也是一种告诉方法对传递给它的参数做什么的方法。如果某个方法采用IEnumerable<T>的参数,它实际上是在说,&#34;我只是要收集你的收藏并通过它进行枚举。我不会添加或删除项目。&#34;

如果参数是List<T>IList<T>,则接收参数的方法可以添加或删除项目。

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

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