title: abstract class and non-nullable value type
body: <p>I'm trying to compile the code (<a href="http://www.bluebytesoftware.com/blog/2004/06/10/MoreIteratorFunWithTheProducerconsumerPattern.aspx" rel="nofollow">More iterator fun with the producer/consumer pattern</a>
  ) proposed by the guru 'Joe Duffy', for class producer / consumer, but this error is occurring:<br></p>
  
  <p>(I'm using visual studio 2010 and net 4.0.3) </p>
  
  <pre>
  Program.cs(37,34): error CS0453: The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'
  Program.cs(11,40): (Related location)
  Program.cs(37,61): error CS0453: The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'
  Program.cs(11,40): (Related location)
  Program.cs(44,53): error CS0453: The type 'T' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'System.Nullable'
  Program.cs(11,40): (Related location)
  </pre>
  
  <p>Is too much for my meager knowledge! Could someone suggest a solution?</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  using System.Threading;
  
  namespace ProducerConsumerClass
  {
      class Program
      {
          public abstract class Producer&lt;T&gt;
          {
              public Producer()
              {
                  worker = new Thread(new ThreadStart(this.ProductionCycle));
              }
              private Queue&lt;T&gt; buffer = new Queue&lt;T&gt;();
              public Thread worker;
              private bool done;
              public bool Done
              {
                  get
                  {
                      return done;
                  }
              }
  
              public IEnumerable&lt;T&gt; ConsumerChannel
              {
                  get
                  {
                      if (done)
                          throw new InvalidOperationException("Production is not currently active");
  
                      while (!done)
                      {
                          Nullable&lt;T&gt; consumed = new Nullable&lt;T&gt;();
  
                          //BUG: compiler crashes when using lock(...) construct within iterator
                          Monitor.Enter(buffer);
                          if (buffer.Count == 0)
                              Monitor.Wait(buffer);
                          if (buffer.Count &gt; 0)
                              consumed = new Nullable&lt;T&gt;(buffer.Dequeue());
                          Monitor.Exit(buffer);
  
                          if (consumed.HasValue)
                              yield return consumed.Value;
                      }
  
                      yield break;
                  }
              }
  
              public void BeginProduction()
              {
                  done = false;
                  worker.Start();
              }
  
              public void EndProduction()
              {
                  done = true;
                  lock (buffer)
                  {
                      Monitor.PulseAll(buffer);
                  }
              }
  
              private void ProductionCycle()
              {
                  while (!done)
                  {
                      T t = ProduceNext();
                      lock (buffer)
                      {
                          buffer.Enqueue(t);
                          Monitor.Pulse(buffer);
                      }
                  }
              }
  
              protected abstract T ProduceNext();
  
          }
  
          public abstract class Consumer&lt;T&gt;
          {
              public Consumer(Producer&lt;T&gt; producer)
              {
                  this.producer = producer;
                  worker = new Thread(new ThreadStart(this.ConsumerCycle));
              }
  
              private Producer&lt;T&gt; producer;
  
              public Thread worker;
  
              private bool done = false;
  
              public bool Done
              {
                  get
                  {
                      return done;
                  }
              }
  
              public void BeginConsumption()
              {
                  done = false;
                  worker.Start();
              }
  
              public void EndConsumption()
              {
                  done = true;
              }
  
              private void ConsumerCycle()
              {
                  foreach (T t in producer.ConsumerChannel)
                  {
                      Consume(t);
                      if (done)
                          break;
                  }
              }
  
              protected abstract void Consume(T t);
          }
  
          class RandomNumberProducer : Producer&lt;int&gt;
          {
              public RandomNumberProducer()
                  : base()
              {
                  rand = new Random();
              }
  
              private Random rand;
  
              protected override int ProduceNext()
              {
                  return rand.Next();
              }
          }
  
          class RandomNumberConsumer : Consumer&lt;int&gt;
          {
              public RandomNumberConsumer(RandomNumberProducer p)
                  : base(p)
              {
              }
  
              private static int counter = 0;
  
              private int id = ++counter;
  
              protected override void Consume(int t)
              {
                  Console.Out.WriteLine("#{0}: consumed {1}", id, t);
              }
          }
  
          static void Main(string[] args)
          {
              RandomNumberProducer p = new RandomNumberProducer();
  
              RandomNumberConsumer c1 = new RandomNumberConsumer(p);
              RandomNumberConsumer c2 = new RandomNumberConsumer(p);
              RandomNumberConsumer c3 = new RandomNumberConsumer(p);
  
              p.BeginProduction();
  
              c1.BeginConsumption();
              c2.BeginConsumption();
              c3.BeginConsumption();
  
              Thread.Sleep(2500);
  
              c3.EndConsumption();
              c2.EndConsumption();
              c1.EndConsumption();
  
              p.EndProduction();
          }
      }
  }
  </code></pre>