1

在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<IEnumerable> 的含义和用途!

以下是我的问题:

  1. 究竟是什么意思?
  2. 计算方法的返回类型应该是什么?
  3. 虽然Calculate方法不是继承自IEnumerable接口,但我应该实现IEnumerable.GetEnumerator()吗?
4

2 回答 2

1

很明显,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”。其他答案非常详细地涵盖了这一点,所以我不会

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

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

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

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

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

--

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

于 2021-01-16T18:08:56.793 回答
1

1、具体是什么意思?

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

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

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

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

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

[ // 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
};

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

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、Calculate方法的返回类型应该是什么?

在 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、虽然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, 1 }, // innerList 2
   new List<int> { 1, 2, 1 } // innerList 3
}
于 2021-01-16T16:47:50.547 回答