嵌套泛型方法的输出是什么?像 IEnumerable<IEnumerable<int>> Calculate() [重复]

What is the output of the method with nested generic type? like IEnumerable<IEnumerable<int>> Calculate() [duplicate]

提问人:elldora 提问时间:1/16/2021 最后编辑:elldora 更新时间:1/18/2021 访问量:166

问:

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

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. Calculate 方法的返回类型应该是什么?
  3. 尽管 Calculate 方法不是从 IEnumerable 接口继承的,但我是否应该实现 IEnumerable.GetEnumerator()?
C# 泛型 接口 IENUMERABLE 嵌套类型

评论

2赞 Heinzi 1/16/2021
这是一个“列表列表”:“哈利波特”是一个字符串。我最喜欢的所有电影的列表是字符串列表。收集我所有“收藏夹列表”的活页夹是字符串列表的列表。
0赞 Heinzi 1/16/2021
在这种特殊情况下,每个都可能包含代表三角形中一的数字。因此,所有行的列表都是 .您可以使用两个嵌套循环遍历它:.IEnumerable<int>IEnumerable<IEnumerable<int>>foreach (IEnumerable<int> row in resultFromCalculate) { foreach (int i in row) { ... } }
1赞 1/16/2021
你也可以理解一个有文件夹的家具:家具包含包含床单的文件夹,这是一个:它是一系列文件集合的界面,就像俄罗斯套娃一样。就像数组数组(锯齿状)或列表列表一样。IENumerable<IEnumerable<Sheet>>
3赞 Tanveer Badar 1/17/2021
Calculate()不是通用方法。

答:

1赞 devNull 1/17/2021 #1

1. 这到底是什么意思?

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

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

在您的例子中,定义基本上可以翻译为:IEnumerable<IEnumerable<int>>

[...]支持对 s 集合的简单迭代,每个集合都支持对整数集合的简单迭代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. 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() 吗?

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

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
}
1赞 Caius Jard 1/17/2021 #2

很明显,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 是事物的整个类型。就像出现在尖括号内并且是一种类型一样,也是一种事物。因为它是一种东西,所以它可以出现在其他东西的尖括号内:stringstringIEnumerable<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))

以下是我的问题:

这究竟是什么意思?

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

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

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

尽管 Calculate 方法不是从 IEnumerable 接口继承的,但我是否应该实现 IEnumerable.GetEnumerator()?

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

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

--

对你来说,这个答案的主要收获应该是巩固术语 - 我认为你可能会对类、方法、接口、返回类型和实现有点困惑。类代表事物,它们实现接口,这意味着可以保证它们具有具有特定名称并返回特定类型的方法。这意味着它们可以以共同的方式处理。但是,类不需要实现一个接口,这样它就可以有一个方法,该方法返回已实现该接口的另一种类型的类