使用yield return返回泛型参数

22 浏览
0 Comments

使用yield return返回泛型参数

希望这不是个重复的问题,在网上找不到相关信息\n我在下面的扩展方法中遇到了一个奇怪的编译时错误:\n错误信息:\n“The body of \'TestBed.EnumerableExtensions.AddRange(TCol, System.Collections.Generic.IEnumerable)\' cannot be an iterator block because \'TCol\' is not an iterator interface type”\n这是否意味着编译器在确定一个方法是否适合使用“yield return”时不考虑泛型约束?\n我在一个使用泛型参数定义集合的类中使用了这个扩展方法。类似这样(还有一些类型转换操作符):\n

public class TestEnum
    where TCol : class, ICollection, new()
{
    TCol _values = default(TCol);
    public TestEnum(IEnumerable values)
    {
        _values = (TCol)(new TCol()).AddRange(values);
    }
    public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
    ...
}

\n然后这样使用(记住我定义了类型转换操作符):\n

TestEnum, string> col = new List() { "Hello", "World" };
string someString = col;
Console.WriteLine(someString);

\n最初,我的扩展方法是这样的:\n

public static IEnumerable AddRange(this IEnumerable e, IEnumerable values)
{
    ...
}

\n这样编译是可以通过的,但结果是:\n“Unhandled Exception: System.InvalidCastException: Unable to cast object of type \'d__61[System.String]\' to type \'System.Collections.Generic.List1[System.String]\'. ”\n有没有其他的方法来做到这一点?\n


\n如您所请求,这里有一个小例子:\n

class Program
{
    public static void Main()
    {
        TestEnum, string> col = new List() { "Hello", "World" };
        string someString = col;
        Console.WriteLine(someString);
    }
}
public class TestEnum
    where TCol : class, ICollection, new()
{
    TCol _values = default(TCol);
    public TestEnum(IEnumerable values)
    {
        _values = (TCol)(new TCol()).AddRange(values);
    }
    public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
    public static implicit operator TItem(TestEnum item)
    {
        return item._values.FirstOrDefault();
    }
    public static implicit operator TestEnum(TCol values)
    {
        return new TestEnum(values);
    }
}
public static class EnumerableExtensions
{
    public static IEnumerable AddRange(this IEnumerable e, IEnumerable values)
    {
        foreach (var cur in e)
        {
            yield return cur;
        }
        foreach (var cur in values)
        {
            yield return cur;
        }
    }
}

\n为了重现编译时异常:\n

class Program
{
    public static void Main()
    {
        TestEnum, string> col = new List() { "Hello", "World" };
        string someString = col;
        Console.WriteLine(someString);
    }
}
public class TestEnum
    where TCol : class, ICollection, new()
{
    TCol _values = default(TCol);
    public TestEnum(IEnumerable values)
    {
        _values = (TCol)(new TCol()).AddRange(values);
    }
    public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
    public static implicit operator TItem(TestEnum item)
    {
        return item._values.FirstOrDefault();
    }
    public static implicit operator TestEnum(TCol values)
    {
        return new TestEnum(values);
    }
}
public static class EnumerableExtensions
{
    public static TCol AddRange(this TCol e, IEnumerable values)
        where TCol : IEnumerable
    {
        foreach (var cur in e)
        {
            yield return cur;
        }
        foreach (var cur in values)
        {
            yield return cur;
        }
    }
}

0
0 Comments

(Returning generic parameter with yield return)这个问题的出现主要是由于在上述代码中,方法`AddRange`的返回类型被定义为`void`,而不是`IEnumerable`。这导致无法通过`yield return`语句返回集合中的元素。

为了解决这个问题,我们需要将方法的返回类型改为`IEnumerable`,并在方法体内使用`yield return`语句来逐个返回集合中的元素。修改后的代码如下:

public static IEnumerable AddRange(this TCol collection, IEnumerable range)
    where TCol : ICollection
{
    var list = collection as List;
    if (list != null)
    {
        list.AddRange(range);
        return list;
    }
    foreach (var item in range)
        collection.Add(item);
    return collection;
}

通过这样的修改,我们可以在调用`AddRange`方法时,使用`yield return`语句从集合中逐个返回元素。这样,我们就可以在方法外部使用`foreach`循环来遍历返回的元素。

,为了在泛型方法中使用`yield return`语句返回集合中的元素,我们需要将方法的返回类型定义为`IEnumerable`,并在方法体内使用`yield return`语句来逐个返回元素。这样,我们就可以在调用该方法时,使用`foreach`循环来遍历返回的元素。

0
0 Comments

问题的出现原因是方法返回的是一个迭代器块,而不是特定的集合类型。迭代器块只能返回IEnumerable或IEnumerator类型,无法强制它返回特定的集合类型,例如List。因此,无法将迭代器块的结果转换为List类型。

解决方法是修改方法实现,不再返回集合,而是直接向集合中添加元素。可以使用ICollection的Add()方法将元素添加到集合中。

以下是修改后的AddRange()方法的示例代码:

public static void AddRange(this ICollection collection, IEnumerable items)
{
    foreach (var item in items)
        collection.Add(item);
}

第二个尝试的代码片段为什么会导致异常呢?迭代器块只能返回IEnumerable类型,无法返回任意的集合类型,这正是你尝试做的事情。而使用Concat()方法时,虽然返回的也不是List类型,但你没有进行类型转换,所以代码可以正常工作。

最后,你的AddRange()方法实现是解决这个问题的方法。非常感谢你的帮助!

文章结束。

0
0 Comments

返回值为泛型参数的yield return语句为什么会出错以及解决方法

在上述代码中,方法M的返回值类型是T,而T是一个泛型参数,并且要求T必须实现IEnumerable接口。然而,编译器只知道如何将M转化为返回类型为IEnumerable的方法,它不知道如何将M转化为返回其他类型的方法。

泛型类型参数T可以是List或者任何实现了IEnumerable接口的类型。C#编译器无法将方法重写为返回它完全不了解的类型。

关于你的方法,TCol的作用是什么?为什么不直接这样写:

public static IEnumerable AddRange(

this IEnumerable s1, IEnumerable s2)

{

foreach(TItem item in s1) yield return item;

foreach(TItem item in s2) yield return item;

}

顺便提一下,这个方法已经存在,它被称为"Concat"。

同意。我最初尝试使用IEnumerable.Concat,但在编译时出现了类似的错误,所以我根据Jared Par的版本为添加到IEnumerable实例的方法(我已经在项目中使用了它,所以我只是克隆了这个函数)编写了自己的版本。这导致我尝试使用泛型来抽象类型,希望它能起作用,但没有成功(导致了这个问题)。我希望能找到一个适用于IEnumerable的解决方案,但如果有一个适用于ICollection的解决方案,我也会接受。

0