--- /dev/null
+\r
+Microsoft Visual Studio Solution File, Format Version 11.00\r
+# Visual Studio 2010\r
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chapter5", "Chapter5\Chapter5.csproj", "{06326E93-0CD5-488A-A283-D26D36968815}"\r
+EndProject\r
+Global\r
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+ Debug|x86 = Debug|x86\r
+ Release|x86 = Release|x86\r
+ EndGlobalSection\r
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+ {06326E93-0CD5-488A-A283-D26D36968815}.Debug|x86.ActiveCfg = Debug|x86\r
+ {06326E93-0CD5-488A-A283-D26D36968815}.Debug|x86.Build.0 = Debug|x86\r
+ {06326E93-0CD5-488A-A283-D26D36968815}.Release|x86.ActiveCfg = Release|x86\r
+ {06326E93-0CD5-488A-A283-D26D36968815}.Release|x86.Build.0 = Release|x86\r
+ EndGlobalSection\r
+ GlobalSection(MonoDevelopProperties) = preSolution\r
+ StartupItem = Chapter5\Chapter5.csproj\r
+ EndGlobalSection\r
+EndGlobal\r
--- /dev/null
+<Properties>
+ <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug|x86" />
+ <MonoDevelop.Ide.Workbench ActiveDocument="Chapter5/Main.cs">
+ <Files>
+ <File FileName="Chapter5/Main.cs" Line="157" Column="4" />
+ <File FileName="Chapter5/MainWindow.cs" Line="15" Column="3" />
+ </Files>
+ </MonoDevelop.Ide.Workbench>
+ <MonoDevelop.Ide.DebuggingService.Breakpoints>
+ <BreakpointStore />
+ </MonoDevelop.Ide.DebuggingService.Breakpoints>
+ <MonoDevelop.Ide.DebuggingService.PinnedWatches />
+</Properties>
\ No newline at end of file
--- /dev/null
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("Chapter5")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("gustavo")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+ <ProductVersion>10.0.0</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{06326E93-0CD5-488A-A283-D26D36968815}</ProjectGuid>
+ <OutputType>WinExe</OutputType>
+ <RootNamespace>Chapter5</RootNamespace>
+ <AssemblyName>Chapter5</AssemblyName>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <PlatformTarget>x86</PlatformTarget>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <DebugType>none</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <PlatformTarget>x86</PlatformTarget>
+ <ConsolePause>false</ConsolePause>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ <Reference Include="gtk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+ <SpecificVersion>False</SpecificVersion>
+ <Package>gtk-sharp-2.0</Package>
+ </Reference>
+ <Reference Include="gdk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+ <SpecificVersion>False</SpecificVersion>
+ <Package>gtk-sharp-2.0</Package>
+ </Reference>
+ <Reference Include="glib-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+ <SpecificVersion>False</SpecificVersion>
+ <Package>glib-sharp-2.0</Package>
+ </Reference>
+ <Reference Include="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+ <SpecificVersion>False</SpecificVersion>
+ <Package>glade-sharp-2.0</Package>
+ </Reference>
+ <Reference Include="pango-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+ <SpecificVersion>False</SpecificVersion>
+ <Package>gtk-sharp-2.0</Package>
+ </Reference>
+ <Reference Include="atk-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f">
+ <SpecificVersion>False</SpecificVersion>
+ <Package>gtk-sharp-2.0</Package>
+ </Reference>
+ <Reference Include="Mono.Posix" />
+ </ItemGroup>
+ <ItemGroup>
+ <EmbeddedResource Include="gtk-gui\gui.stetic">
+ <LogicalName>gui.stetic</LogicalName>
+ </EmbeddedResource>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="gtk-gui\generated.cs" />
+ <Compile Include="MainWindow.cs" />
+ <Compile Include="gtk-gui\MainWindow.cs" />
+ <Compile Include="Main.cs" />
+ <Compile Include="AssemblyInfo.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file
--- /dev/null
+using System;
+using Gtk;
+using System.IO;
+using System.Collections.Generic;
+
+namespace Chapter5
+{
+ class MainClass
+ {
+ delegate Stream StreamFactory();
+ delegate void SampleDelegate(string x);
+ delegate void MethodInvoker();
+
+ public static void Main (string[] args)
+ {
+ Application.Init ();
+
+ MainWindow win = new MainWindow ();
+
+
+ /**
+ *
+ * Listing 5.1 Subscribing to three of a button's events.
+ */
+ Button buttonA = new Button ("Click me 5.1");
+ buttonA.Pressed += new EventHandler (LogPlainEvent);
+ buttonA.KeyPressEvent += new KeyPressEventHandler (LogKeyEvent);
+ win.Add (buttonA);
+
+
+ /**
+ *
+ * Listing 5.2 Demonstration of method group conversion and delegate contravariance.
+ */
+ Button buttonB = new Button ("Click me 5.2");
+ buttonB.Pressed += LogPlainEvent;
+ buttonB.KeyPressEvent += LogPlainEvent;
+ win.Add (buttonB);
+
+
+ /**
+ *
+ * Listing 5.3 Demonstration of covariance of return types for delegates.
+ */
+ StreamFactory factoryA = GenerateSampleData;
+
+ using (Stream stream = factoryA()) {
+ int data;
+
+ while ((data = stream.ReadByte()) != -1) {
+ Console.WriteLine (data);
+ }
+ }
+
+
+ /**
+ *
+ * Listing 5.4 Demonstration of braking change between C# 1 and C# 2.
+ */
+ Derived x = new Derived ();
+ SampleDelegate factoryB = new SampleDelegate (x.CandidateAction);
+ factoryB ("test");
+
+
+ /**
+ *
+ * Listing 5.5 Anonymous methods used with the Action<T> delegate type.
+ */
+ Action<string> printReverse = delegate(string text) {
+ char[] chars = text.ToCharArray ();
+ Array.Reverse (chars);
+ Console.WriteLine (new String (chars));
+ };
+
+ Action<int> printRoot = delegate(int number) {
+ Console.WriteLine (Math.Sqrt (number));
+ };
+
+ Action<IList<double>> printMean = delegate(IList<double> numbers) {
+ double total = 0;
+ foreach (double value in numbers) {
+ total += value;
+ }
+ Console.WriteLine (total / numbers.Count);
+ };
+
+ printReverse ("Hello Gus");
+ printRoot (2);
+ printMean (new double[] {1.5, 2.5, 3.5, 4.5, 5, 7, 10.1});
+
+
+ /**
+ *
+ * Listing 5.7 Returning a value from an anonymous method.
+ */
+ Predicate<int> isEven = delegate(int number) {
+ return number % 2 == 0;
+ };
+ Console.WriteLine (isEven (1));
+ Console.WriteLine (isEven (4));
+
+
+ /**
+ *
+ * Listing 5.8 Using anonymous methods to sort files simply.
+ */
+ SortAndShowFiles ("Sorted by name:", delegate(FileInfo f1, FileInfo f2) {
+ return f1.Name.CompareTo (f2.Name);
+ }
+ );
+
+ SortAndShowFiles ("Sorted by length:", delegate(FileInfo f1, FileInfo f2) {
+ return f1.Length.CompareTo (f2.Length);
+ }
+ );
+
+
+ /**
+ *
+ * Listing 5.9 Subscribing to event with anonymous methods that ignore parameters.
+ */
+ Button buttonC = new Button ("Click me 5.9");
+ buttonC.Pressed += delegate {
+ Console.WriteLine ("LogPlain");
+ };
+ buttonC.KeyPressEvent += delegate {
+ Console.WriteLine ("LogKey");
+ };
+ win.Add (buttonC);
+
+
+ /**
+ *
+ * Listing 5.10 Examples of variable kinds with respect to anonymous methods.
+ */
+ int outerVariable = 5;
+ string capturedVariable = "captured";
+
+ if (DateTime.Now.Hour == 1) {
+ int normalLocalVariable = DateTime.Now.Minute;
+ Console.WriteLine (normalLocalVariable);
+ }
+
+ MethodInvoker method = delegate() {
+ string anonLocal = " local to anonymous method";
+ Console.WriteLine (capturedVariable + anonLocal);
+ };
+ method ();
+
+
+ /**
+ *
+ * Listing 5.11 Accessing a variable both inside and outside an anonymous method.
+ */
+ string captured = "before methodB is created";
+
+ MethodInvoker methodB = delegate {
+ Console.WriteLine (captured);
+ captured = "changed by methodB";
+ };
+ captured = "directly before methodB is invoked";
+ methodB ();
+
+ Console.WriteLine (captured);
+
+ captured = "before second invocation";
+ methodB ();
+
+
+ /**
+ *
+ * Listing 5.12 Demonstration of a captured variable having its lifetime extended.
+ */
+ MethodInvoker methodC = CreateDelegateInstance ();
+ methodC ();
+ methodC ();
+
+
+ /**
+ *
+ * Listing 5.13 Capturing multiple variable instantiations with multiple delegates.
+ */
+ List<MethodInvoker> list = new List<MethodInvoker> ();
+
+ // The variable declared by the initial part of the loop is only instantiated once.
+ for (int index = 0; index < 5; index++) {
+ // Because counter is declared inside the loop, it's instantiated for each iteration.
+ int counter = index * 10;
+ list.Add (delegate {
+ Console.WriteLine (counter);
+ counter++;
+ }
+ );
+ }
+
+ foreach (MethodInvoker t in list) {
+ t ();
+ }
+ list [0] ();
+ list [0] ();
+ list [0] ();
+
+ list [1] ();
+
+
+ /**
+ *
+ * Listing 5.14 Capturing variables in different scopes. Warning: nasty code ahead!
+ */
+ MethodInvoker[] delegates = new MethodInvoker[2];
+
+ int outside = 0;
+
+ for (int i = 0; i < 2; i++) {
+ // Because inside is declared inside the loop, it's instantiated for each iteration.
+ int inside = 0;
+
+ delegates[i] = delegate {
+ Console.WriteLine ("({0},{1})", outside, inside);
+ outside++;
+ inside++;
+ };
+ }
+
+ MethodInvoker first = delegates[0];
+ MethodInvoker second = delegates[1];
+
+ first();
+ first();
+ first();
+
+ second();
+ second();
+
+
+
+ win.ShowAll ();
+ Application.Run ();
+ }
+
+
+ static void LogPlainEvent (object sender, EventArgs e)
+ {
+ Console.WriteLine("LogPlain");
+ }
+
+
+ static void LogKeyEvent (object sender, KeyPressEventArgs e)
+ {
+ Console.WriteLine("LogKey");
+ }
+
+
+ static MemoryStream GenerateSampleData ()
+ {
+ byte[] buffer = new byte[16];
+ for (int i = 0; i < buffer.Length; i++) {
+ buffer[i] = (byte) i;
+ }
+
+ return new MemoryStream(buffer);
+ }
+
+
+ public void CandidateAction(string x)
+ {
+ Console.WriteLine("Snippet.CandidateAction");
+ }
+
+
+ public class Derived:MainClass
+ {
+ public void CandidateAction(object o)
+ {
+ Console.WriteLine("Derived.CandidateAction");
+ }
+ }
+
+ static void SortAndShowFiles (string title, Comparison<FileInfo> sortOrder)
+ {
+ FileInfo[] files = new DirectoryInfo (@"/").GetFiles ();
+
+ Array.Sort (files, sortOrder);
+
+ Console.WriteLine (title);
+ foreach (FileInfo file in files) {
+ Console.WriteLine(" {0} ({1} bytes)", file.Name, file.Length);
+ }
+ }
+
+ static MethodInvoker CreateDelegateInstance ()
+ {
+ int counter = 5;
+
+ MethodInvoker ret = delegate
+ {
+ Console.WriteLine(counter);
+ counter++;
+ };
+
+ ret();
+ return ret;
+ }
+ }
+
+}
--- /dev/null
+using System;
+using Gtk;
+
+public partial class MainWindow: Gtk.Window
+{
+ public MainWindow (): base (Gtk.WindowType.Toplevel)
+ {
+ Build ();
+ }
+
+ protected void OnDeleteEvent (object sender, DeleteEventArgs a)
+ {
+ Application.Quit ();
+ a.RetVal = true;
+ }
+}
--- /dev/null
+
+// This file has been generated by the GUI designer. Do not modify.
+
+public partial class MainWindow
+{
+ private global::Gtk.UIManager UIManager;
+
+ protected virtual void Build ()
+ {
+ global::Stetic.Gui.Initialize (this);
+ // Widget MainWindow
+ this.UIManager = new global::Gtk.UIManager ();
+ global::Gtk.ActionGroup w1 = new global::Gtk.ActionGroup ("Default");
+ this.UIManager.InsertActionGroup (w1, 0);
+ this.AddAccelGroup (this.UIManager.AccelGroup);
+ this.Name = "MainWindow";
+ this.Title = global::Mono.Unix.Catalog.GetString ("MainWindow");
+ this.WindowPosition = ((global::Gtk.WindowPosition)(4));
+ if ((this.Child != null)) {
+ this.Child.ShowAll ();
+ }
+ this.DefaultWidth = 400;
+ this.DefaultHeight = 300;
+ this.Show ();
+ this.DeleteEvent += new global::Gtk.DeleteEventHandler (this.OnDeleteEvent);
+ }
+}
--- /dev/null
+
+// This file has been generated by the GUI designer. Do not modify.
+namespace Stetic
+{
+ internal class Gui
+ {
+ private static bool initialized;
+
+ internal static void Initialize (Gtk.Widget iconRenderer)
+ {
+ if ((Stetic.Gui.initialized == false)) {
+ Stetic.Gui.initialized = true;
+ }
+ }
+ }
+
+ internal class ActionGroups
+ {
+ public static Gtk.ActionGroup GetActionGroup (System.Type type)
+ {
+ return Stetic.ActionGroups.GetActionGroup (type.FullName);
+ }
+
+ public static Gtk.ActionGroup GetActionGroup (string name)
+ {
+ return null;
+ }
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<stetic-interface>
+ <configuration>
+ <images-root-path>..</images-root-path>
+ <target-gtk-version>2.12</target-gtk-version>
+ </configuration>
+ <import>
+ <widget-library name="glade-sharp, Version=2.12.0.0, Culture=neutral, PublicKeyToken=35e10195dab3c99f" />
+ <widget-library name="../bin/Debug/Chapter5.exe" internal="true" />
+ </import>
+ <widget class="Gtk.Window" id="MainWindow" design-size="400 300">
+ <action-group name="Default" />
+ <property name="MemberName" />
+ <property name="Title" translatable="yes">MainWindow</property>
+ <property name="WindowPosition">CenterOnParent</property>
+ <signal name="DeleteEvent" handler="OnDeleteEvent" />
+ <child>
+ <placeholder />
+ </child>
+ </widget>
+</stetic-interface>
\ No newline at end of file
--- /dev/null
+\r
+Microsoft Visual Studio Solution File, Format Version 11.00\r
+# Visual Studio 2010\r
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Chapter6", "Chapter6\Chapter6.csproj", "{DD91EB8A-A3C0-4A8D-A529-82621728B868}"\r
+EndProject\r
+Global\r
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution\r
+ Debug|x86 = Debug|x86\r
+ Release|x86 = Release|x86\r
+ EndGlobalSection\r
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution\r
+ {DD91EB8A-A3C0-4A8D-A529-82621728B868}.Debug|x86.ActiveCfg = Debug|x86\r
+ {DD91EB8A-A3C0-4A8D-A529-82621728B868}.Debug|x86.Build.0 = Debug|x86\r
+ {DD91EB8A-A3C0-4A8D-A529-82621728B868}.Release|x86.ActiveCfg = Release|x86\r
+ {DD91EB8A-A3C0-4A8D-A529-82621728B868}.Release|x86.Build.0 = Release|x86\r
+ EndGlobalSection\r
+ GlobalSection(MonoDevelopProperties) = preSolution\r
+ StartupItem = Chapter6\Chapter6.csproj\r
+ EndGlobalSection\r
+EndGlobal\r
--- /dev/null
+<Properties>
+ <MonoDevelop.Ide.Workspace ActiveConfiguration="Debug|x86" />
+ <MonoDevelop.Ide.Workbench ActiveDocument="Chapter6/Main.cs">
+ <Files>
+ <File FileName="Chapter6/Main.cs" Line="58" Column="45" />
+ </Files>
+ </MonoDevelop.Ide.Workbench>
+ <MonoDevelop.Ide.DebuggingService.Breakpoints>
+ <BreakpointStore />
+ </MonoDevelop.Ide.DebuggingService.Breakpoints>
+ <MonoDevelop.Ide.DebuggingService.PinnedWatches />
+</Properties>
\ No newline at end of file
--- /dev/null
+using System.Reflection;
+using System.Runtime.CompilerServices;
+
+// Information about this assembly is defined by the following attributes.
+// Change them to the values specific to your project.
+
+[assembly: AssemblyTitle("Chapter6")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("")]
+[assembly: AssemblyCopyright("gustavo")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}".
+// The form "{Major}.{Minor}.*" will automatically update the build and revision,
+// and "{Major}.{Minor}.{Build}.*" will update just the revision.
+
+[assembly: AssemblyVersion("1.0.*")]
+
+// The following attributes are used to specify the signing key for the assembly,
+// if desired. See the Mono documentation for more information about signing.
+
+//[assembly: AssemblyDelaySign(false)]
+//[assembly: AssemblyKeyFile("")]
+
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
+ <ProductVersion>10.0.0</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{DD91EB8A-A3C0-4A8D-A529-82621728B868}</ProjectGuid>
+ <OutputType>Exe</OutputType>
+ <RootNamespace>Chapter6</RootNamespace>
+ <AssemblyName>Chapter6</AssemblyName>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <PlatformTarget>x86</PlatformTarget>
+ <Externalconsole>true</Externalconsole>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
+ <DebugType>none</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release</OutputPath>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <PlatformTarget>x86</PlatformTarget>
+ <Externalconsole>true</Externalconsole>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="System" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Main.cs" />
+ <Compile Include="AssemblyInfo.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
+</Project>
\ No newline at end of file
--- /dev/null
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace Chapter6
+{
+ class MainClass
+ {
+ static readonly string Padding = new string(' ', 30);
+
+ public static void Main (string[] args)
+ {
+
+ /**
+ *
+ * Listings 6.1 and 6.2 Skeleton of th new collection type, with no iterator implementation.
+ */
+ object[] values = {"a", "b", "c", "d", "e"};
+ IterationSampleBad badCollection = new IterationSampleBad(values, 3);
+ try {
+ foreach(object x in badCollection)
+ {
+ Console.WriteLine(x);
+ }
+ } catch(NotImplementedException e) {
+ Console.WriteLine("Listings 6.1 and 6.2 exception: {0}", e);
+ }
+
+
+ /**
+ *
+ * Listings 6.3 Nested class implementing the collection's iterator.
+ */
+ IterationSample collection = new IterationSample(values, 3);
+ foreach(object x in collection)
+ {
+ Console.WriteLine(x);
+ }
+
+
+ /**
+ *
+ * Listings 6.4 Iterating through the sample collection with C# 2 and yield return.
+ */
+ IterationSampleYield yieldCollection = new IterationSampleYield(values, 3);
+ foreach(object x in yieldCollection)
+ {
+ Console.WriteLine(x);
+ }
+
+
+ /**
+ *
+ * Listings 6.5 Showing the sequence of calls between an iterator and its caller.
+ */
+ IEnumerable<int> iterable = CreateEnumerable();
+ IEnumerator<int> iterator = iterable.GetEnumerator();
+ Console.WriteLine("Starting to iterate");
+ }
+
+ static IEnumerable<int> CreateEnumerable ()
+ {
+ Console.WriteLine ("{0}Start of CreateEnumerable()", Padding);
+
+ for (int i=0; i < 3; i++) {
+ Console.WriteLine("{0}About to yield {1}", Padding, i);
+ yield return i;
+ Console.WriteLine("{0}After yield", Padding);
+ }
+
+ Console.WriteLine("{0}Yielding final value", Padding);
+ yield return -1;
+
+ Console.WriteLine("{0}End of CreateEnumerable()", Padding);
+ }
+ }
+
+
+ public class IterationSampleBad : IEnumerable
+ {
+ object[] values;
+ int startingPoint;
+
+ public IterationSampleBad (object[] values, int startingPoint)
+ {
+ this.values = values;
+ this.startingPoint = startingPoint;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ throw new NotImplementedException();
+ }
+ }
+
+
+ // The same as the Java style of creating a custom Iterator:
+ // see http://stackoverflow.com/questions/5849154/can-we-write-our-own-iterator-in-java
+ // (basically it is the same)
+ public class IterationSample : IEnumerable
+ {
+ object[] values;
+ int startingPoint;
+
+ public IterationSample (object[] values, int startingPoint)
+ {
+ this.values = values;
+ this.startingPoint = startingPoint;
+ }
+
+ public IEnumerator GetEnumerator()
+ {
+ // If GetEnumerator is called several times, serveral independent iterators should be returned.
+ return new IterationSampleIterator(this);
+ }
+
+ // Let's create another nested class to implement the iterator itself.
+ public class IterationSampleIterator : IEnumerator
+ {
+ IterationSample parent;
+ // Iterators are stateful. We need to store some state somewhere (the position in this example)
+ int position;
+
+ internal IterationSampleIterator (IterationSample parent)
+ {
+ this.parent = parent;
+ position = -1;
+ }
+
+ public bool MoveNext ()
+ {
+ if (position != parent.values.Length) {
+ position++;
+ }
+ return position < parent.values.Length;
+ }
+
+ public object Current {
+ get {
+ if (position == -1 ||
+ position == parent.values.Length) {
+ throw new InvalidOperationException();
+ }
+ int index = position + parent.startingPoint;
+ index = index % parent.values.Length;
+ return parent.values[index];
+ }
+ }
+
+ public void Reset()
+ {
+ position = -1;
+ }
+ }
+ }
+
+
+ // The C# 2.0 style! Rocks!
+ public class IterationSampleYield : IEnumerable
+ {
+ object[] values;
+ int startingPoint;
+
+ public IterationSampleYield (object[] values, int startingPoint)
+ {
+ this.values = values;
+ this.startingPoint = startingPoint;
+ }
+
+ public IEnumerator GetEnumerator ()
+ {
+ // * Whenever MoveNext is called, it has to execute code from the GetEnumerator
+ // method until you are ready to provide the next value (in other words, until you
+ // hit a yield return statement)
+ // * When the Current property is used, it has to return the last value you yielded.
+ // * It has to know when you have finished yielding values so that MoveNext can return false.
+ for (int index = 0; index < values.Length; index++) {
+ yield return values[(index + startingPoint) % values.Length];
+ }
+ }
+ }
+}