在C#中使用的yield关键字是什么?

在C#中使用的yield关键字是什么?,c#,yield,C#,Yield,在问题中,其中一个答案包含以下代码片段: IEnumerable<object> FilteredList() { foreach(object item in FullList) { if(IsItemInPartialList(item)) yield return item; } } IEnumerable FilteredList() { foreach(完整列表中的对象项) { if(IsItemInPart

在问题中,其中一个答案包含以下代码片段:

IEnumerable<object> FilteredList()
{
    foreach(object item in FullList)
    {
        if(IsItemInPartialList(item))
            yield return item;
    }
}
IEnumerable FilteredList()
{
foreach(完整列表中的对象项)
{
if(IsItemInPartialList(项目))
收益回报项目;
}
}

yield关键字在那里做什么?我已经在几个地方看到了它的引用,还有一个问题,但我还没有完全弄清楚它到底做了什么。我习惯于从一个线程向另一个线程屈服的角度来考虑屈服,但这在这里似乎并不相关。

迭代。它创建了一个“隐藏”状态机,它可以记住您在函数的每个附加循环中的位置,并从中提取信息。

这是一种非常简单和容易的方法,可以为您的对象创建一个枚举。编译器创建一个类,该类包装您的方法,并在本例中实现IEnumerable。如果没有yield关键字,您必须创建一个实现IEnumerable的对象。

它正在生成可枚举序列。它实际上是创建本地IEnumerable序列并将其作为方法结果返回。直观地说,关键字从函数返回一个值而不离开它,即在代码示例中,它返回当前的
值,然后恢复循环。更正式地说,编译器使用它为迭代器生成代码。迭代器是返回
IEnumerable
对象的函数。这个词有几个方面。

这个
yield
上下文关键字实际上在这里起了很大作用

函数返回一个实现
IEnumerable
接口的对象。如果调用函数在该对象上启动foreach
ing,则再次调用该函数,直到它“屈服”。这是在C#2.0中引入的语法糖。在早期版本中,您必须创建自己的
IEnumerable
IEnumerator
对象来执行类似的操作

理解这样的代码最简单的方法是键入一个示例,设置一些断点,然后看看会发生什么。尝试单步执行此示例:

public void Consumer()
{
    foreach(int i in Integers())
    {
        Console.WriteLine(i.ToString());
    }
}

public IEnumerable<int> Integers()
{
    yield return 1;
    yield return 2;
    yield return 4;
    yield return 8;
    yield return 16;
    yield return 16777216;
}

最近,陈雷蒙(Raymond Chen)也发表了一系列关于收益率关键字的有趣文章


虽然它名义上用于轻松实现迭代器模式,但可以推广到状态机。引用Raymond没有任何意义,最后一部分还链接到其他用途(但Entin博客中的示例是esp good,展示了如何编写异步安全代码)。

它试图引入一些Ruby的优点:)
概念:这是一些示例Ruby代码,用于打印数组的每个元素

 rubyArray = [1,2,3,4,5,6,7,8,9,10]
    rubyArray.each{|x| 
        puts x   # do whatever with x
    }
数组的每个方法实现控制权交给调用方(“puts x”),数组的每个元素整齐地表示为x。然后调用方可以对x执行任何需要执行的操作

然而,.Net在这里并不是一帆风顺的。。C#似乎将yield与IEnumerable结合起来,在某种程度上迫使您在调用者中编写foreach循环,如Mendelt的响应所示。不那么优雅

//calling code
foreach(int i in obCustomClass.Each())
{
    Console.WriteLine(i.ToString());
}

// CustomClass implementation
private int[] data = {1,2,3,4,5,6,7,8,9,10};
public IEnumerable<int> Each()
{
   for(int iLooper=0; iLooper<data.Length; ++iLooper)
        yield return data[iLooper]; 
}
//调用代码
foreach(obCustomClass.Each()中的int i)
{
Console.WriteLine(i.ToString());
}
//自定义类实现
私有int[]数据={1,2,3,4,5,6,7,8,9,10};
公共IEnumerable Each()
{
对于(int iLooper=0;iLooper简单地说,C#yield关键字允许多次调用称为迭代器的代码体,该代码体知道如何在完成之前返回,当再次调用时,会在停止的地方继续-即,它帮助迭代器在迭代器连续返回的序列中每个项透明地变为有状态我打电话来


在JavaScript中,相同的概念被称为生成器。

收益有两大用途

  • 它有助于在不创建临时集合的情况下提供自定义迭代

  • 它有助于进行有状态的迭代。


  • 为了更直观地解释以上两点,我创建了一个简单的视频,您可以观看它

    yield return
    与枚举器一起使用。在每次调用yield语句时,控件都返回给调用方,但它确保被调用方的状态保持不变。因此,当调用方枚举下一个元素时,我t在
    yield
    语句之后立即在被调用方方法from语句中继续执行

    让我们试着用一个例子来理解这一点。在这个例子中,对应于我提到的每一行执行流的顺序

    static void Main(string[] args)
    {
        foreach (int fib in Fibs(6))//1, 5
        {
            Console.WriteLine(fib + " ");//4, 10
        }            
    }
    
    static IEnumerable<int> Fibs(int fibCount)
    {
        for (int i = 0, prevFib = 0, currFib = 1; i < fibCount; i++)//2
        {
            yield return prevFib;//3, 9
            int newFib = prevFib + currFib;//6
            prevFib = currFib;//7
            currFib = newFib;//8
        }
    }
    
    static void Main(字符串[]args)
    {
    foreach(Fibs(6)中的int fib)//1,5
    {
    Console.WriteLine(fib+“”);//4,10
    }            
    }
    静态IEnumerable Fibs(int fibCount)
    {
    对于(inti=0,prevFib=0,currFib=1;i

    此外,每个枚举都会维护状态。假设我对
    Fibs()
    方法进行了另一次调用,那么该方法的状态将被重置。

    乍一看,收益率返回值是.NETsugar,返回IEnumerable

    在没有收益的情况下,集合中的所有项目都将一次创建:

    class SomeData
    {
        public SomeData() { }
    
        static public IEnumerable<SomeData> CreateSomeDatas()
        {
            return new List<SomeData> {
                new SomeData(), 
                new SomeData(), 
                new SomeData()
            };
        }
    }
    
    class-SomeData
    {
    public SomeData(){}
    静态公共IEnumerable CreateSomeDatas()
    {
    返回新列表{
    新建SomeData(),
    新建SomeData(),
    新数据
    };
    }
    }
    
    使用yield的代码相同,它逐项返回:

    class SomeData
    {
        public SomeData() { }
    
        static public IEnumerable<SomeData> CreateSomeDatas()
        {
            yield return new SomeData();
            yield return new SomeData();
            yield return new SomeData();
        }
    }
    
    class-SomeData
    {
    public SomeData(){}
    静态公共IEnumerable CreateSomeDatas()
    {
    返回新的SomeData();
    返回新的SomeData();
    返回新的SomeData();
    }
    }
    
    使用yield的优点是,如果使用数据的函数只需要collect的第一项
    class SomeData
    {
        public SomeData() { }
    
        static public IEnumerable<SomeData> CreateSomeDatas()
        {
            yield return new SomeData();
            yield return new SomeData();
            yield return new SomeData();
        }
    }
    
        public class ContactListStore : IStore<ContactModel>
        {
            public IEnumerable<ContactModel> GetEnumerator()
            {
                var contacts = new List<ContactModel>();
                Console.WriteLine("ContactListStore: Creating contact 1");
                contacts.Add(new ContactModel() { FirstName = "Bob", LastName = "Blue" });
                Console.WriteLine("ContactListStore: Creating contact 2");
                contacts.Add(new ContactModel() { FirstName = "Jim", LastName = "Green" });
                Console.WriteLine("ContactListStore: Creating contact 3");
                contacts.Add(new ContactModel() { FirstName = "Susan", LastName = "Orange" });
                return contacts;
            }
        }
    
        static void Main(string[] args)
        {
            var store = new ContactListStore();
            var contacts = store.GetEnumerator();
    
            Console.WriteLine("Ready to iterate through the collection.");
            Console.ReadLine();
        }
    
    public class ContactYieldStore : IStore<ContactModel>
    {
        public IEnumerable<ContactModel> GetEnumerator()
        {
            Console.WriteLine("ContactYieldStore: Creating contact 1");
            yield return new ContactModel() { FirstName = "Bob", LastName = "Blue" };
            Console.WriteLine("ContactYieldStore: Creating contact 2");
            yield return new ContactModel() { FirstName = "Jim", LastName = "Green" };
            Console.WriteLine("ContactYieldStore: Creating contact 3");
            yield return new ContactModel() { FirstName = "Susan", LastName = "Orange" };
        }
    }
    
    static void Main(string[] args)
    {
        var store = new ContactYieldStore();
        var contacts = store.GetEnumerator();
    
        Console.WriteLine("Ready to iterate through the collection.");
        Console.ReadLine();
    }
    
    static void Main(string[] args)
    {
        var store = new ContactYieldStore();
        var contacts = store.GetEnumerator();
        Console.WriteLine("Ready to iterate through the collection");
        Console.WriteLine("Hello {0}", contacts.First().FirstName);
        Console.ReadLine();
    }
    
    public static IEnumerable<int> testYieldb()
    {
        for(int i=0;i<3;i++) yield return 4;
    }
    
    public static IEnumerable<int> testYieldb()
    {
        yield return 4;
        console.WriteLine("abc");
        yield return 4;
    }
    
    static void Main(string[] args)
    {
        testA();
        Console.Write("try again. the above won't execute any of the function!\n");
    
        foreach (var x in testA()) { }
    
    
        Console.ReadLine();
    }
    
    
    
    // static List<int> testA()
    static IEnumerable<int> testA()
    {
        Console.WriteLine("asdfa");
        yield return 1;
        Console.WriteLine("asdf");
    }
    
    IEnumerable<int> IteratorBlock()
    {
        Console.WriteLine("Begin");
        yield return 1;
        Console.WriteLine("After 1");
        yield return 2;
        Console.WriteLine("After 2");
        yield return 42;
        Console.WriteLine("End");
    }
    
    foreach (var i in IteratorBlock())
        Console.WriteLine(i);
    
    Begin 1 After 1 2 After 2 42 End
    IEnumerator<int> enumerator = null;
    try
    {
        enumerator = IteratorBlock().GetEnumerator();
        while (enumerator.MoveNext())
        {
            var i = enumerator.Current;
            Console.WriteLine(i);
        }
    }
    finally
    {
        enumerator?.Dispose();
    }
    
    var evenNumbers = IteratorBlock().Where(i => i%2 == 0);
    
    foreach (var evenNumber in evenNumbers)
        Console.WriteLine(eventNumber);
    
    public static IEnumerable<int> CreateCollectionWithList()
    {
        var list =  new List<int>();
        list.Add(10);
        list.Add(0);
        list.Add(1);
        list.Add(2);
        list.Add(20);
    
        return list;
    }
    
    public static IEnumerable<int> CreateCollectionWithYield()
    {
        yield return 10;
        for (int i = 0; i < 3; i++) 
        {
            yield return i;
        }
    
        yield return 20;
    }
    
    var listItems = CreateCollectionWithList();
    var yieldedItems = CreateCollectionWithYield();