具有嵌套泛型类型的方法的输出是什么?像 IEnumerable<IEnumerable<int>> Calculate()

问题描述

exercism.io中的Pascal三角练习,方法签名如下:

using System;
using System.Collections.Generic;

public static class PascalsTriangle
{
    public static IEnumerable<IEnumerable<int>> Calculate(int rows)
    {
        …
    }

}

很明显,Calculate 方法一个具有嵌套泛型类型的方法。 我知道 IEnumerable 是通用接口,它指示方法的类型。并且 IEnumerable 是一个通用接口,它适用于整数值类型。但是我无法理解嵌套泛型类型 IEnumerable 的含义和用途!

这是我的问题:

  1. 这到底是什么意思?
  2. Calculate 方法的返回类型应该是什么?
  3. 尽管Calculate 方法不是从IEnumerable 接口继承的,但我应该实现IEnumerable.GetEnumerator() 吗?

解决方法

1.具体是什么意思?

为了理解这一点,了解 IEnumerable<T> 是什么或其代表什么可能会有所帮助。 the docs 中的定义是:

公开枚举器,它支持对指定类型的集合进行简单迭代

就您的 IEnumerable<IEnumerable<int>> 而言,定义基本上可以翻译为:

[...] 支持对 IEnumerable<int> 集合的简单迭代,每个都支持对整数集合的简单迭代

这导致可以由“集合的集合”表示的嵌套迭代行为。帕斯卡三角形就是一个很好的例子,因为你有:

[ // Outer collection 
[1],// Inner collection 1
[1,1],// Inner collection 2
[1,2,// Inner collection 3
... // Inner collection n
] 

代码中的一个例子是:

IEnumerable<int> innerCollection1 = new List<int> { 1 };
IEnumerable<int> innerCollection2 = new List<int> { 1,1 };
IEnumerable<int> innerCollection3 = new List<int> { 1,1 };
IEnumerable<IEnumerable<int>> outerCollection = new List<IEnumerable<int>>
{
    innerCollection1,innerCollection2,innerCollection3
};

然后要获取内部的实际值,您需要遍历 externalCollection 中的每个 innerCollection。例如:

foreach (IEnumerable<int> innerCollection in outerCollection)
{
    foreach (int value in innerCollection)
    {
        Console.Write(value);
        Console.Write(" ");
    }
    Console.WriteLine();
}

输出如下:

1
1 1
1 2 1

2.计算方法的返回类型应该是什么?

在 C# 中,您可以使用任何实现 IEnumerable<int> 的东西来表示它,例如列表列表:

new List<List<int>>
{
    new List<int> { 1 },new List<int> { 1,1 },// new List<int> { ... },}

或数组数组:

new int[][]
{
    new int[] { 1 },new int[] { 1,// new int[] { ... },}

或数组列表:

new List<int[]>
{
    new int[] { 1 },}

或者一个列表数组:

new List<int>[]
{
    new List<int> { 1 },}

等等。等

3.虽然Calculate方法不是继承自IEnumerable接口,但我应该实现IEnumerable.GetEnumerator()吗?

您只需要在实现 IEnumerable.GetEnumerator() 接口的自定义类型中实现 IEnumerable。在您的情况下,您可以只返回一个已经实现该接口的类型(几乎是 System.Collections.Generic 中的任何内容),如上所示。显然,您只需要根据传递给该方法的 rows 数量动态构建该实例,一个简单的示例如下所示:

public static IEnumerable<IEnumerable<int>> Calculate(int rows)
{
    List<List<int>> outerList = new List<List<int>>();
    for (int i = 0; i < rows; i++)
    {
        List<int> innerList = new List<int>();
        // logic to build innerList
        outerList.Add(innerList);
    }
    return outerList;
}

调用时,例如将 3 作为 rows 传递,应导致:

List<List<int>> outerList = new List<List<int>>
{
   new List<int> { 1 },// innerList 1
   new List<int> { 1,// innerList 2
   new List<int> { 1,1 } // innerList 3
}
,

很明显,Calculate 方法是一个泛型方法

Calculate 不是通用方法。泛型方法的名称后面有尖括号,其中包含一个或多个“变量”,这些变量是您将在方法中引用的未知类型,这意味着它们可能如下所示:

MethodName<TypeReference>()
MethodName<TypeReference>(TypeReference param1)
TypeReference MethodName<TypeReference>()
MethodName<TypeReference1,TypeReference2>(TypeReference2 param1,TypeReference1 param2)

您在尖括号中所做的是为将在运行时使用的类型建立名称别名。当使用实际类型时,您使用该别名的任何地方都将表现得如同您使用实际类型一样:

T MethodName<T>(T param1);

//if you call it with a string,i.e. var v = MethodName<string>("hello");
//it will behave as if you defined it like:
string MethodName(string param1)

//if you call it with an int,it will behave as if you defined it as:
int MethodName(int param1)

我知道 IEnumerable 是通用接口,它指示方法的类型。

方法没有“类型”。它们可能返回有类型的东西,也可能有特定类型的参数,但方法本身没有类型

但我无法理解嵌套泛型类型 IEnumerable 的含义和用途!

只是欣赏它是另一种类型,并且可以重复很长时间:

IEnumerable<string>

这是一种类型。它不是一个字符串,它是一个包含字符串集合的东西,它可以被枚举。 IEnumerable 是事物的整个类型。就像 string 出现在尖括号内并且 string 是一种类型一样,IEnumerable<string> 也是一种类型。因为它是一种可以出现在其他东西的尖括号内的东西:

IList<string> //string is a type. It can appear inside angle brackets
IList<IEnumerable<string>> //IEnumerable<string> is a type,it can appear inside angle brackets

VB.NET 的语法可能更清晰:

IList(Of String)
IList(Of IEnumerable(Of String))

这是我的问题:

具体是什么意思?

这是一个“x of y of z”。如果它是 IEnumerable<IList<IEnumerable<string>>>,它将是“w of x of y of z”。其他答案非常详细地介绍了这一点,所以我不会

Calculate 方法的返回类型应该是什么?

可以枚举的东西,充满了可以枚举的子事物。而那些可枚举的子事物必须是 int 的集合。在你的“x of y of z”中,x 和 y 是集合,z 是一个整数。

虽然Calculate方法不是继承自IEnumerable接口,但我应该实现IEnumerable.GetEnumerator()吗?

您不继承接口,而是实现它。您的类没有实现 IEnumerable,因为它似乎不需要。如果您想提供 GetEnumerator 方法并能够说 foreach var whatever in myPascalsTriangle,那么您可以实现 IEnumerable。

您的方法被声明为返回一些其他已经实现了 IEnumerable 的类型,因此您只需提供一个实现它的类型来遵守它。您不必在包含该方法的此类上实现它(就像您不必每次要使用字符串时都实现字符串一样)

--

这个答案的主要内容应该是巩固术语 - 我认为您可能对类、方法、接口、返回类型和实现有点困惑。类代表事物,它们实现接口,这意味着它们可以保证具有特定名称的方法并返回特定类型。这意味着它们可以以一种通用的方式被对待..但是没有任何需要一个类来实现一个接口,这样它就可以有一个方法来返回已经实现了该接口的另一种类型的类