Asked  7 Months ago    Answers:  5   Viewed   37 times

In the How Can I Expose Only a Fragment of IList<> question one of the answers had the following code snippet:

IEnumerable<object> FilteredList()
{
    foreach(object item in FullList)
    {
        if(IsItemInPartialList(item))
            yield return item;
    }
}

What does the yield keyword do there? I've seen it referenced in a couple places, and one other question, but I haven't quite figured out what it actually does. I'm used to thinking of yield in the sense of one thread yielding to another, but that doesn't seem relevant here.

 Answers

35

The yield contextual keyword actually does quite a lot here.

The function returns an object that implements the IEnumerable<object> interface. If a calling function starts foreaching over this object, the function is called again until it "yields". This is syntactic sugar introduced in C# 2.0. In earlier versions you had to create your own IEnumerable and IEnumerator objects to do stuff like this.

The easiest way understand code like this is to type-in an example, set some breakpoints and see what happens. Try stepping through this example:

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;
}

When you step through the example, you'll find the first call to Integers() returns 1. The second call returns 2 and the line yield return 1 is not executed again.

Here is a real-life example:

public IEnumerable<T> Read<T>(string sql, Func<IDataReader, T> make, params object[] parms)
{
    using (var connection = CreateConnection())
    {
        using (var command = CreateCommand(CommandType.Text, sql, connection, parms))
        {
            command.CommandTimeout = dataBaseSettings.ReadCommandTimeout;
            using (var reader = command.ExecuteReader())
            {
                while (reader.Read())
                {
                    yield return make(reader);
                }
            }
        }
    }
}
Tuesday, June 1, 2021
 
Laimoncijus
answered 7 Months ago
74

For both C# and Java, "volatile" tells the compiler that the value of a variable must never be cached as its value may change outside of the scope of the program itself. The compiler will then avoid any optimisations that may result in problems if the variable changes "outside of its control".

Saturday, June 12, 2021
 
relyt
answered 6 Months ago
22

Because you want to keep the Stream open for the duration of the enumeration AND deal with exceptions AND properly close the file handle either way, I don't think you can use a regular enumeration shortcut (the iterator block, yield-return/yield-break).

Instead, just do what the compiler would have done for you and add some:

By implementing IEnumerator yourself, you can also add IDisposable

public class LazyStream : IEnumerable<string>, IDisposable
{
  LazyEnumerator le;

  public LazyStream(FileInfo file, Encoding encoding)
  {
    le = new LazyEnumerator(file, encoding);
  }

  #region IEnumerable<string> Members
  public IEnumerator<string> GetEnumerator()
  {
    return le;
  }
  #endregion

  #region IEnumerable Members
  System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
  {
    return le;
  }
  #endregion

  #region IDisposable Members
  private bool disposed = false;

  public void Dispose()
  {
    Dispose(true);

    GC.SuppressFinalize(this);
  }

  protected virtual void Dispose(bool disposing)
  {
    if (!this.disposed)
    {
      if (disposing)
      {
        if (le != null) le.Dispose();
      }

      disposed = true;
    }
  }
  #endregion

  class LazyEnumerator : IEnumerator<string>, IDisposable
  {
    StreamReader streamReader;
    const int chunksize = 1024;
    char[] buffer = new char[chunksize];

    string current;

    public LazyEnumerator(FileInfo file, Encoding encoding)
    {
      try
      {
        streamReader = new StreamReader(file.OpenRead(), encoding);
      }
      catch
      {
        // Catch some generator related exception
      }
    }

    #region IEnumerator<string> Members
    public string Current
    {
      get { return current; }
    }
    #endregion

    #region IEnumerator Members
    object System.Collections.IEnumerator.Current
    {
      get { return current; }
    }

    public bool MoveNext()
    {
      try
      {
        if (streamReader.Peek() >= 0)
        {
          int readCount = streamReader.Read(buffer, 0, chunksize);

          current = new string(buffer, 0, readCount);

          return true;
        }
        else
        {
          return false;
        }
      }
      catch
      {
        // Trap some iteration error
      }
    }

    public void Reset()
    {
      throw new NotSupportedException();
    }
    #endregion

    #region IDisposable Members
    private bool disposed = false;

    public void Dispose()
    {
      Dispose(true);

      GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
      if (!this.disposed)
      {
        if (disposing)
        {
          if (streamReader != null) streamReader.Dispose();
        }

        disposed = true;
      }
    }
    #endregion
  }
}

I didn't test this, but I think it's close.

used like this:

using (var fe = new LazyStream(new FileInfo("c:\data.log"), Encoding.ASCII))
{
  foreach (var chunk in fe)
  {
    Console.WriteLine(chunk);
  }
}

EDIT: I had totally forgotten to add the try-catch block placements. Oops.

Tuesday, August 3, 2021
 
Otiel
answered 4 Months ago
54

Even though there is no specific documentation, there was a similar question here

Usually, .vs folder is required by Visual Studio to store opened documents, breakpoints, and other information about state of your solution. which means It contains typical files like,

  • Temporary caches used by Roslyn for IntelliSense.
  • IIS Express applicationHost.config file.
  • Many other possible files (you are welcome to edit this answer to include what you know of).
Tuesday, August 3, 2021
 
Lawrence Taur
answered 4 Months ago
25

See this post for a good explanation of the issues with using and yield. Because you return in enumerator, the using block will already have destroyed the context before anything is accessed. The answers have good solutions, basically, either make the wrapper method an enumerator, or build a list instead.

Also it's usually more practical to have using around the reader, not the connection, and use CommandBehavior.CloseConnection to ensure resources are released when the reader's done. Though it doesn't really matter in your situation, if you ever return a data reader from a method, this will ensure the connection is closed properly when the reader is disposed.

   using(SqlDataReader reader = 
             command.ExecuteReader(CommandBehavior.CloseConnection)) {
        while (reader.Read())
        {
            yield reader[0];
        }
   }
Sunday, September 19, 2021
 
RustyStatistician
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share