返回带有yield return的泛型参数

M.B*_*ock 1 c# generics ienumerable typecast-operator

希望这不是一个骗局,找不到任何相关的在线

我在以下扩展方法中遇到奇怪的编译时错误:

public static TCol AddRange<TCol, TItem>(this TCol e, IEnumerable<TItem> values) 
    where TCol: IEnumerable<TItem>
{
    foreach (var cur in e)
    {
        yield return cur;
    }
    foreach (var cur in values)
    {
        yield return cur;
    }
}
Run Code Online (Sandbox Code Playgroud)

错误:

'TestBed.EnumerableExtensions.AddRange(TCol,System.Collections.Generic.IEnumerable)'的主体不能是迭代器块,因为'TCol'不是迭代器接口类型

这是否意味着编译器在确定方法是否符合yield return使用条件时不考虑通用约束?

我在一个使用泛型参数定义集合的类中使用此扩展方法.类似的东西(除了一些类型转换操作符):

public class TestEnum<TCol, TItem>
    where TCol : class, ICollection<TItem>, new()
{
    TCol _values = default(TCol);

    public TestEnum(IEnumerable<TItem> values)
    {
        _values = (TCol)(new TCol()).AddRange(values);
    }
    public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }

    ...
}
Run Code Online (Sandbox Code Playgroud)

反过来,使用像(记得我定义了类型转换操作符):

TestEnum<List<string>, string> col = new List<string>() { "Hello", "World" };
string someString = col;
Console.WriteLine(someString);
Run Code Online (Sandbox Code Playgroud)

最初,我的扩展方法看起来像:

public static IEnumerable<TItem> AddRange<TItem>(this IEnumerable<TItem> e, IEnumerable<TItem> values)
{
    ...
}
Run Code Online (Sandbox Code Playgroud)

哪个编译但导致:

未处理的异常:System.InvalidCastException:无法转换类型为"<AddRange> d _6 1[System.String]' to type 'System.Collections.Generic.List1 [System.String]"的对象.

有没有其他方法可以做到这一点?


根据要求,这是一个小样本:

class Program
{
    public static void Main()
    {
        TestEnum<List<string>, string> col = new List<string>() { "Hello", "World" };
        string someString = col;

        Console.WriteLine(someString);
    }
}

public class TestEnum<TCol, TItem>
    where TCol : class, ICollection<TItem>, new()
{
    TCol _values = default(TCol);

    public TestEnum(IEnumerable<TItem> values)
    {
        _values = (TCol)(new TCol()).AddRange(values);
    }
    public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
    public static implicit operator TItem(TestEnum<TCol, TItem> item)
    {
        return item._values.FirstOrDefault();
    }
    public static implicit operator TestEnum<TCol, TItem>(TCol values)
    {
        return new TestEnum<TCol, TItem>(values);
    }
}
public static class EnumerableExtensions
{
    public static IEnumerable<TItem> AddRange<TItem>(this IEnumerable<TItem> e, IEnumerable<TItem> values)
    {
        foreach (var cur in e)
        {
            yield return cur;
        }
        foreach (var cur in values)
        {
            yield return cur;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

要重新编译编译时异常:

class Program
{
    public static void Main()
    {
        TestEnum<List<string>, string> col = new List<string>() { "Hello", "World" };
        string someString = col;

        Console.WriteLine(someString);
    }
}

public class TestEnum<TCol, TItem>
    where TCol : class, ICollection<TItem>, new()
{
    TCol _values = default(TCol);

    public TestEnum(IEnumerable<TItem> values)
    {
        _values = (TCol)(new TCol()).AddRange(values);
    }
    public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
    public static implicit operator TItem(TestEnum<TCol, TItem> item)
    {
        return item._values.FirstOrDefault();
    }
    public static implicit operator TestEnum<TCol, TItem>(TCol values)
    {
        return new TestEnum<TCol, TItem>(values);
    }
}
public static class EnumerableExtensions
{
    public static TCol AddRange<TCol, TItem>(this TCol e, IEnumerable<TItem> values)
        where TCol : IEnumerable<TItem>
    {
        foreach (var cur in e)
        {
            yield return cur;
        }
        foreach (var cur in values)
        {
            yield return cur;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 6

让我们简化一下:

static T M() where T : IEnumerable<int>
{
    yield return 1;
}
Run Code Online (Sandbox Code Playgroud)

为什么这是非法的?

出于同样的原因,这是非法的:

static List<int> M() 
{
    yield return 1;
}
Run Code Online (Sandbox Code Playgroud)

编译器只知道如何将M转换为返回的方法IEnumerable<something>.它不知道如何将M转换为返回其他任何东西的方法.

您的泛型类型参数T可以是List<int>实现的无限多种其他类型IEnumerable<T>.C#编译器不知道如何将该方法重写为一个返回它不知道的类型的方法.

现在,关于你的方法:首先是TCol的功能是什么?为什么不说:

public static IEnumerable<TItem> AddRange<TItem>(
  this IEnumerable<TItem> s1, IEnumerable<TItem> s2)
{
  foreach(TItem item in s1) yield return item;
  foreach(TItem item in s2) yield return item;
}
Run Code Online (Sandbox Code Playgroud)

顺便提一下,这种方法已经存在; 它被称为"康卡特".


svi*_*ick 5

我不确定你想要完成什么,你的方法当然看起来不像AddRange(),因为它不会给任何集合添加任何东西.

但是如果你编写一个迭代器块,它将返回一个IEnumerable<T>(或IEnumerator<T>).它返回的实际运行时类型是编译器生成的,并且无法强制它返回某些特定的集合,例如List<T>.

从您的示例中,AddRange()根本不返回List<T>,这就是您无法将结果强制转换为该类型的原因.而且没有办法让迭代器块返回List<T>.

如果要创建一个向集合添加内容的方法,可能意味着您需要调用Add(),而不是从方法返回其他一些集合:

public static void AddRange<T>(
    this ICollection<T> collection, IEnumerable<T> items)
{
    foreach (var item in items)
        collection.Add(item);
}
Run Code Online (Sandbox Code Playgroud)