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



在 exercism.io 的帕斯卡三角形练习中,方法签名如下:

using System;
using System.Collections.Generic;
public static class PascalsTriangle
{
public static IEnumerable<IEnumerable<int>> Calculate(int rows)
{
…
}
}

很明显,计算方法是一种具有嵌套泛型类型的方法。 我知道IE无数<...> 是泛型接口,它指示方法的类型。IEnumerable 是一个通用接口,它适用于整数值类型。但是我无法理解嵌套泛型类型IEnumerable的含义和目的!

以下是我的问题:

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

1.这到底是什么意思?

为了掌握这一点,了解IEnumerable<T>是什么或它代表什么可能会有所帮助。文档中的定义是:

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

在你的IEnumerable<IEnumerable<int>>的情况下,定义基本上可以翻译成:

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

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

[ // Outer collection 
[1], // Inner collection 1
[1,1], // Inner collection 2
[1,2,1], // 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, 2, 1 };
IEnumerable<IEnumerable<int>> outerCollection = new List<IEnumerable<int>>
{
innerCollection1,
innerCollection2, 
innerCollection3
};

然后,要获得内部的实际值,您需要遍历 outerCollection 中的每个 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> { 1, 2, 1 },
// new List<int> { ... },
}

或者数组数组:

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

或数组列表:

new List<int[]>
{
new int[] { 1 },
new int[] { 1, 1 },
new int[] { 1, 2, 1 },
// new int[] { ... },
}

或列表数组:

new List<int>[]
{
new List<int> { 1 },
new List<int> { 1, 1 },
new List<int> { 1, 2, 1 },
// new List<int> { ... },
}

等等。

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

您只需要在实现IEnumerable接口的自定义类型中实现IEnumerable.GetEnumerator()。在您的情况下,您可以只返回一个已经实现该接口的类型(几乎是 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, 1 }, // innerList 2
new List<int> { 1, 2, 1 } // innerList 3
}

很明显,计算方法是一种通用方法

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

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>>>它将是"x of y of z"的w of x。其他答案非常详细地涵盖了这一点,所以我不会

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

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

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

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

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

--

这个答案给你的主要收获应该是巩固术语 - 我想你可能会对类、方法、接口、返回类型、实现感到有些困惑。类表示事物,它们实现接口,这意味着可以保证它们具有具有某些名称并返回某些类型的方法。这意味着可以用共同的方式对待它们。但是,不需要类来实现接口,以便它可以有一个返回已经实现该接口的另一种类型的类的方法。

最新更新