从IEnumerable到List的无效转换

时间:2018-05-04 21:23:38

标签: c# casting type-conversion

我正在Unity3D项目中使用C#脚本,我正在尝试获取字符串列表并获取排列的2D列表。以下列方式使用this answer's GetPermutations()

List<string> ingredientList = new List<string>(new string[] { "ingredient1", "ingredient2", "ingredient3" });

List<List<string>> permutationLists = GetPermutations(ingredientList, ingredientList.Count);

但它会引发隐式转换错误:

IEnumerable<IEnumerable<string>> to List<List<string>> ... An explicit conversion exists (are you missing a cast)?

所以我查看了一些地方,例如here,并提出了以下修改:

List<List<string>> permutationLists = GetPermutations(ingredientList, ingredientList.Count).Cast<List<string>>().ToList();

但它在运行时中断,在内部处理,并允许它继续而不指示失败 - 可能是因为它在Unity3D中运行。 这是我在停止调试脚本后在Unity3D中看到的内容:

InvalidCastException: Cannot cast from source type to destination type.
System.Linq.Enumerable+<CreateCastIterator>c__Iterator0`1[System.Collections.Generic.List`1[System.String]].MoveNext ()
System.Collections.Generic.List`1[System.Collections.Generic.List`1[System.String]].AddEnumerable (IEnumerable`1 enumerable) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:128)
System.Collections.Generic.List`1[System.Collections.Generic.List`1[System.String]]..ctor (IEnumerable`1 collection) (at /Users/builduser/buildslave/mono/build/mcs/class/corlib/System.Collections.Generic/List.cs:65)
System.Linq.Enumerable.ToList[List`1] (IEnumerable`1 source)

我认为这仍然是错误的,所以我也尝试了以下方法以及更多我不记得的方法:

List<List<string>> permutationLists = GetPermutations(ingredientList, ingredientList.Count).Cast<List<List<string>>>();

List<List<string>> permutationLists = GetPermutations(ingredientList.AsEnumerable(), ingredientList.Count);

以及在方法调用之前使用括号显式转换,就像在C或Java中一样,仍然无济于事。

那么我应该如何将GetPermutations()函数的结果投射到List<List<string>>?或者,我怎么能修改函数只返回List<List<string>>,因为我不需要它为泛型类型工作?我试着自己修改方法如下:

List<List<string>> GetPermutations(List<string> items, int count)
{
    int i = 0;
    foreach(var item in items)
    {
        if(count == 1)
            yield return new string[] { item };
        else
        {
            foreach(var result in GetPermutations(items.Skip(i + 1), count - 1))
                yield return new string[] { item }.Concat(result);
        }

        ++i;
    }
}

但是,从函数名中删除<T>后,它会断言主体不能是迭代器块。我之前没有使用C#的经验,而且我在强类型语言中生成了模板函数,所以任何解释/帮助都是值得赞赏的。

我不确定如何看待这个问题,所以如果这是重复的,请在此处发布,我会立即删除此帖。

2 个答案:

答案 0 :(得分:8)

  

那么我应该如何从GetPermutations()函数中转换结果来获取List<List<string>>

最佳解决方案:不要。为什么需要首先将序列转换为列表?将其保留为一系列序列。

如果你必须:

GetPermutations(...).Select(s => s.ToList()).ToList()

如果你想修改原始方法,只需做同样的事情:

IEnumerable<List<string>> GetPermutations(List<string> items, int count)
{
    int i = 0;
    foreach(var item in items)
    {
        if(count == 1)
            yield return new List<T>() { item };
        else
        {
            foreach(var result in GetPermutations(items.Skip(i + 1), count - 1))
                yield return (new string[] {item}.Concat(result)).ToList();
        }

        ++i;
    }
}

然后执行GetPermutations(whatever).ToList()并列出一系列列表。但同样,不要这样做。如果可能,请将所有内容保留在序列中。

  

我想将序列转换为列表,以便我可以按字母顺序对元素进行排序,并将它们重新连接为有序的单个逗号分隔字符串。

好的,然后那样做。让我们将您的方法重写为扩展方法Permute()。让我们制作一些新的单线方法:

static public string CommaSeparate(this IEnumerable<string> items) =>
  string.Join(",", items);

static public string WithNewLines(this IEnumerable<string> items) =>
  string.Join("\n", items);

static public IEnumerable<string> StringSort(this IEnumerable<string> items) =>
  items.OrderBy(s => s);

然后我们有以下内容 - 我将按照我们的方式注释类型:

string result = 
  ingredients                    // List<string>
  .Permute()                     // IEnumerable<IEnumerable<string>>
  .Select(p => p.StringSort())   // IEnumerable<IEnumerable<string>>
  .Select(p => p.CommaSeparate())// IEnumerable<string>
  .WithNewLines();                   // string

我们已经完成了。 看看当你制作一个做得好但做得好的方法时,代码是多么清晰明了。看看按顺序保存所有内容是多么容易。

答案 1 :(得分:1)

您的问题与C#和.net类型系统的几个方面有关。我将尝试提供简单的解释,并提供链接作为更正式的答案。

因此,根据您的描述,procedure TForm1.btn1Click(Sender: TObject); begin AnimateWindow(Panel1.Handle, 800, AW_SLIDE or AW_VER_NEGATIVE or AW_HIDE); end; procedure TForm1.btn2Click(Sender: TObject); begin AnimateWindow(Panel1.Handle, 800, AW_SLIDE or AW_VER_POSITIVE or AW_ACTIVATE); end; 看起来像GetPermutations(ingredientList, ingredientList.Count);,但您尝试将此结果分配给另一种类型的变量,伪代码:

IEnumerable<IEnumerable<string>>

List<List<string>> = IEnumerable<IEnumerable<string>>; 实现List<T>,因此通常可以进行此分配:

IEnumerable<T>

但问题是,在您的情况下,左侧的IEnumerable<T> = List<T>; 与右侧的T不同。

    {li> for T IEnumerable<IEnumerable<string>>T。 {li> for IEnumerable<string> List<List<string>>T

要解决您的问题,我们应该更改代码,使其在左侧和右侧具有相同的List<string>,即将T转换为TList<string>

您可以通过以下方式将IEnumerable<string>转换为T

List<string>

但一般来说,在所有地方使用List并不是一个好主意。仅在您真正需要的地方使用列表。重点:

  • IEnumerable<List<string> GetPermutationsList(List<string> items, int count) { return GetPermutations(items, count).Select(x=>x.ToList()) } IEnumerable<List<string>> permutationLists = GetPermutations(ingredientList.AsEnumerable(), ingredientList.Count); // or List<List<string>> permutationLists = GetPermutations(ingredientList.AsEnumerable(), ingredientList.Count).ToList(); 提供了最低限度的功能(仅限枚举),应该符合您的目标。
  • IEnumerable<T>(List实现它)提供最大功能(按索引添加,删除,“随机”访问)。你真的需要最大的功能吗?
  • 同样使用IList <T>会导致大数据的内存不足问题。
  • ToList()只是强制立即进行查询评估并返回ToList()

一些有用的信息:covariance-contr-varianceListcasting