35

吸うという私の永遠の探求では、「利回り」ステートメントを理解しようとしていますが、同じエラーに遭遇し続けています。

[somemethod]の本体は、 'System.collections.generic.list <Aclass>'がイテレーターインターフェイスタイプではないため、イテレーターブロックにはなりません。

これは私が立ち往生したコードです:

foreach (XElement header in headersXml.Root.Elements()){
    yield return (ParseHeader(header));                
}

私は何を間違っていますか?イテレータでyieldを使用できませんか? それでは、ポイントは何ですか?この例では、それList<ProductMixHeader>はイテレータ インターフェイス タイプではないと述べています。 ProductMixHeaderはカスタム クラスですListが、イテレータ インターフェイス型だと思いますよね?

--編集--
すべての迅速な回答に感謝します。
この質問はそれほど新しいものではなく、同じリソースが次々と出てくることはわかっています。戻り型として
返せると思っていたのですが、怠け者ではないのでできません。戻り値の型を変更して問題を解決しました:DList<AClass>List<T>IEnumerable<T>

IEnumerable<T>やや関連する質問 (新しいスレッドを開く価値はありません):とにかく .ToList() に行くケースの 99% が確実な場合、戻り値の型として与える価値はありますか? パフォーマンスへの影響はどうなりますか?

4

8 に答える 8

32

yield returnを使用するメソッドは、次の 2 つのインターフェイスのいずれかを返すものとして宣言する必要があります。

IEnumerable<SomethingAppropriate>
IEnumerator<SomethingApropriate>

( IEnumerator を指摘してくれたJonMarcに感謝)

例:

public IEnumerable<AClass> YourMethod()
{
    foreach (XElement header in headersXml.Root.Elements())
    {
        yield return (ParseHeader(header));                
    }
}

yield はデータの遅延プロデューサーであり、最初のアイテムが取得された後にのみ別のアイテムを生成しますが、リストを返すとすべてが一度に返されます。

したがって、違いがあり、メソッドを正しく宣言する必要があります。

詳細については、非常に便利なリンクが含まれているJon's answer hereを参照してください。

于 2008-11-25T14:16:41.570 に答える
15

難しいトピックです。一言で言えば、これは IEnumerable とその仲間を実装する簡単な方法です。コンパイラはステート マシンを構築し、パラメータとローカル変数を新しいクラスのインスタンス変数に変換します。複雑なもの。

これに関するリソースがいくつかあります。

于 2008-11-25T14:23:00.733 に答える
8

「yield」は、イテレータ ブロックを作成します。これは、 または のいずれIEnumerable[<T>]かを実装できるコンパイラによって生成されたクラスですIEnumerator[<T>]Jon Skeet は、 C# in Depthの第 6 章で、これについて非常に優れた (そして無料の) 議論を行っています。

ただし、基本的には、「yield」を使用するには、メソッドがIEnumerable[<T>]orを返す必要がありますIEnumerator[<T>]。この場合:

public IEnumerable<AClass> SomeMethod() {
    // ...
    foreach (XElement header in headersXml.Root.Elements()){
        yield return (ParseHeader(header));                
    }
}
于 2008-11-25T14:21:10.597 に答える
3

Reflectoryieldを使用して、実際に何ができるかを確認することを強くお勧めします。yield を使用すると、コンパイラが生成するクラスの完全なコードを見ることができます。また、低レベルの結果 (まあ、中間のレベルだと思います)。

于 2008-11-25T14:27:29.770 に答える
3

List は Ienumerable を実装します。

これは、あなたが学ぼうとしていることに光を当てるかもしれない例です。6ヶ月くらい書いた

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YieldReturnTest
{
    public class PrimeFinder
    {
        private Boolean isPrime(int integer)
        {
            if (0 == integer)
                return false;

            if (3 > integer)
                return true;

            for (int i = 2; i < integer; i++)
            {
                if (0 == integer % i)
                    return false;
            }
            return true;
        }

        public IEnumerable<int> FindPrimes()
        {
            int i;

            for (i = 1; i < 2147483647; i++)
            {
                if (isPrime(i))
                {
                    yield return i;
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            PrimeFinder primes = new PrimeFinder();

            foreach (int i in primes.FindPrimes())
            {
                Console.WriteLine(i);
                Console.ReadLine();
            }

            Console.ReadLine();
            Console.ReadLine();
        }
    }
}
于 2008-11-25T14:21:03.857 に答える
2

を理解するには、いつandを使用するかyieldを理解する必要があります(どちらかを使用する必要があるため)。次の例は、違いを理解するのに役立ちます。IEnumeratorIEnumerable

IEnumerator<int>まず、次のクラスを見てください。これは 2つのメソッドを実装していますIEnumerable<int>。2 つのメソッドのコードは似ていますが、使用方法に大きな違いがあることをお見せします。

// 2 iterators, one as IEnumerator, one as IEnumerable
public class Iterator
{
    public static IEnumerator<int> IterateOne(Func<int, bool> condition)
    {
        for(var i=1; condition(i); i++) { yield return i; }     
    }
    public static IEnumerable<int> IterateAll(Func<int, bool> condition)
    {
        for(var i=1; condition(i); i++) { yield return i; }     
    }
}

を使用しているIterateOne場合は、次の操作を実行できます。

    // 1. Using IEnumerator allows to get item by item
    var i=Iterator.IterateOne(x => true); // iterate endless
    // 1.a) get item by item
    i.MoveNext(); Console.WriteLine(i.Current);
    i.MoveNext(); Console.WriteLine(i.Current);
    // 1.b) loop until 100
    int j; while (i.MoveNext() && (j=i.Current)<=100) { Console.WriteLine(j); }

1.a) プリント:

1
2

1.b) プリント:

3
4
...
100

1.a) ステートメントが実行された直後にカウントを継続するためです。

を使ってアイテムごとに進められることがわかりますMoveNext()


対照的に、より快適にするためにLINQステートメントもIterateAll使用できます。foreach

    // 2. Using IEnumerable makes looping and LINQ easier   
    var k=Iterator.IterateAll(x => x<100); // limit iterator to 100
    // 2.a) Use a foreach loop
    foreach(var x in k){ Console.WriteLine(x); } // loop
    // 2.b) LINQ: take 101..200 of endless iteration
    var lst=Iterator.IterateAll(x=>true).Skip(100).Take(100).ToList(); // LINQ: take items
    foreach(var x in lst){ Console.WriteLine(x); } // output list

2.a) プリント:

1
2
...
99

2.b) プリント:

101
102
...
200


注:IEnumerator<T>とはジェネリックであるためIEnumerable<T>、どの型でも使用できます。ただし、簡単にするためにint、例では type を使用しましたT

つまり、戻り値の型の 1 つIEnumerator<ProductMixHeader>またはIEnumerable<ProductMixHeader>(質問で言及したカスタム クラス) を使用できます。

List<ProductMixHeader>はこれらのインターフェイスを実装していません。これが、そのように使用できない理由です。ただし、例 2.b)は、そこからリストを作成する方法を示しています。

追加してリストを作成している場合.ToList()、メモリ内のすべての要素のリストを作成することをIEnumerable意味しますが、要素の遅延作成を許可します-パフォーマンスの観点から、要素がジャストインタイムで列挙されることを意味します-できるだけ遅く、ただし を使用するとすぐに.ToList()、すべての要素がメモリ内に作成されます。LINQ は、舞台裏でこの方法でパフォーマンスを最適化しようとします。

すべての例の DotNetFiddle

于 2015-06-17T09:13:20.297 に答える
1

@Ian Pの回答は、収量とそれが使用される理由を理解するのに大いに役立ちました。yield の (主な) 使用例の 1 つは、「in」キーワードの後の「foreach」ループで、完全に完成したリストを返さないことです。一度に完全なリストを返す代わりに、各「foreach」ループで 1 つの項目 (次の項目) のみが返されます。そのため、そのような場合に利回りを使用してパフォーマンスを向上させることができます。次のことをよりよく理解するために、@ Ian Pのコードを書き直しました。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace YieldReturnTest
{
    public class PrimeFinder
    {
        private Boolean isPrime(int integer)
        {
            if (0 == integer)
                return false;

            if (3 > integer)
                return true;

            for (int i = 2; i < integer; i++)
            {
                if (0 == integer % i)
                    return false;
            }
            return true;
        }

        public IEnumerable<int> FindPrimesWithYield()
        {
            int i;

            for (i = 1; i < 2147483647; i++)
            {
                if (isPrime(i))
                {
                    yield return i;
                }
            }
        }

        public IEnumerable<int> FindPrimesWithoutYield()
        {
            var primes = new List<int>();
            int i;
            for (i = 1; i < 2147483647; i++)
            {
                if (isPrime(i))
                {
                    primes.Add(i);
                }
            }
            return primes;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            PrimeFinder primes = new PrimeFinder();

            Console.WriteLine("Finding primes until 7 with yield...very fast...");
            foreach (int i in primes.FindPrimesWithYield()) // FindPrimesWithYield DOES NOT iterate over all integers at once, it returns item by item
            {
                if (i > 7)
                {
                    break;
                }
                Console.WriteLine(i);
                //Console.ReadLine();

            }

            Console.WriteLine("Finding primes until 7 without yield...be patient it will take lonkg time...");
            foreach (int i in primes.FindPrimesWithoutYield()) // FindPrimesWithoutYield DOES iterate over all integers at once, it returns the complete list of primes at once
            {
                if (i > 7)
                {
                    break;
                }
                Console.WriteLine(i);
                //Console.ReadLine();
            }

            Console.ReadLine();
            Console.ReadLine();
        }
    }
}
于 2014-07-11T07:42:05.147 に答える
0

これを使用している方法はどのように見えますか? これは、ループだけで使用できるとは思いません。

例えば...

public IEnumerable<string> GetValues() {
    foreach(string value in someArray) {
        if (value.StartsWith("A")) { yield return value; }
    }
}
于 2008-11-25T14:16:48.420 に答える