2016-04-30 34 views
1

我是C#的初学者程序员,我刚刚开始使用IEnumerable的练习。为什么你可以使用IEnumerable作为方法的返回类型或者在构造函数中

我想我明白的代码,但我挣扎理解为什么你会不会使用这种方式

例1

考虑以下隔离代码.....

private List<Card> cards; 

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

创建甲板对象并调用这个构造函数的代码是...

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

如果有人先不介意确认低于我的理解......

  • 我们创造型卡的数组,并用卡
  • 我们通过卡[]数组中的甲板构造填充当创建一个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; 
} 

这是来自同一个类中的方法。再次,我不确定为什么这是作为一个IEnumerable返回,当我们仍然可以做一个字符串数组的foreach。

例3

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

这是非常noobish,但有人可以给我解释一下这是什么意思吗?它是IEnumerable类型的公共属性吗?当你使用一个接口作为属性类型时,它与其他类型有何不同?例如字符串

这与LINQ稍后在代码中一起使用,这在我的学习中还没有得到。可能这个例子表明,如果我们只想遍历某些东西,IEnumerable可以被使用吗?

非常感谢,

+0

可枚举是某些序列操作的泛化 - 这些操作包括由数组提供的子集。所有数组(或列表)*实现* IEnumerable。 – user2864740

回答

4

返回接口(在这种情况下IEnumerable)或接受它们是面向对象编程中的一个核心方法。这混合了面向对象的三个核心原则,特别是抽象

在长期支持应用程序中,您经常进行更改。在你的应用程序开始时,你需要固定大小的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); 
} 

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

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

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

private List<Card> cards; 

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

在某些时候,你的应用程序的增长。你需要玩独一无二的牌。你的应用程序的一部分创建了一张HashSet卡片。你不能将它传递给构造函数。该死的构造函数正在接受List's。如果您将构造函数更改为接受HashSet's,则会破坏来自应用程序的另一部分的逻辑,该部分通过List

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

你真正需要的是采取Cards的集合,可以是foreach'd并在稍后枚举它以用于另一种用法。然后你只要告诉你的应用程序的其他部分 - “伙计们,给我一个可以枚举的对象,我真的不在乎它是否是SetListArray”。

private IEnumerable<Card> cards; 

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

另一种方式也是如此。它降低了复杂性。你只要告诉你的消费者,你会给他们一个Card名字的集合来枚举。他们需要列出并看到一些名片。他们不需要更改名称,添加新卡或其他任何东西。

你不公开你的实现细节。你只要给自己的对象机会,尽量不要理解其内部结构。这种隐藏信息的方式可以作为封装的一部分。

1

例1

我们创造型卡的数组,并用卡

填充

不是一个问题,但正确的。

创建 卡座对象时,我们将卡片[]数组传递给卡座构造函数。即使构造函数参数包含IEnumerable类型的参数 ,但它被认为是有效的,因为数组实现了 IEnumerable。在这里发生了演员?

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

列表卡片使用在 中传递的IEnumerable参数初始化为重载列表构造函数。再次,这是一个演员或一个 转换某种?

如果您检查,List包含一个IEnumerable构造函数。由于您的构造函数是通过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; 
} 

这是从相同类的方法。再次,我不知道为什么这是 作为一个IEnumerable返回时,当我们仍然可以做一个字符串 数组的foreach。

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

例3

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

这是非常noobish,但有人可以给我解释一下,这是 说什么?它是IEnumerable类型的公共属性吗?当你使用一个 接口作为属性类型时,它与其他类型有什么不同? 例如字符串

这是用于LINQ后面的代码,我还没有在我的 学习。可能这个例子表明,如果我们只希望 迭代某些东西,可以使用IEnumerable?

这是一个公开的IEnumerable属性,你有一个默认的支持者说,等了IEnumerable保证它可以遍历,这是一个相当有力的保障,因为它允许搜索和更多的东西。只是因为你可以迭代它并不意味着项目是连续的或其他任何东西,因此实现可能会有所不同。这是接口的优势之一。

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

1

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

数组,列表和其他集合类型全部实现IEnumerable。将方法参数声明为IEnumerable表示该方法不会“关心”实际类型,只要它实现IEnumerable即可。

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

这也是一种告诉方法期望对传递给它的参数做些什么的方法。如果一个方法采用IEnumerable<T>的参数,它实际上是说:“我只是想拿你的收藏并枚举它,我不会添加或删除项目。”

如果参数是List<T>IList<T>那么接收它的方法可能会添加或删除项目。

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

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

相关问题