1.9. Query Evaluation Time

It is important to understand when the query is evaluated at run time. In Listing 1-1 nothing happens in query execution until the ObjectDumper's Write method is called. Listing 1-8 looks at the code behind this method:

Example 1-8. The Core Method of the ObjectDumper Helper Class
private void WriteObject(string prefix, object o) {
  if (o == null || o is ValueType || o is string) {
    WriteIndent();
    Write(prefix);
    WriteValue(o);
    WriteLine();
  }
  else if (o is IEnumerable) {
    foreach (object element in (IEnumerable)o) {
      if (element is IEnumerable && !(element is string)) {
        WriteIndent();
        Write(prefix);
        Write("...");
        WriteLine();
        if (level < depth) {
          level++;
          WriteObject(prefix, element);
          level--;

}
     }
     else {
       WriteObject(prefix, element);
     }
   }
 }
 else {
   MemberInfo[] members = null;
   members = o.GetType().GetMembers(BindingFlags.Public |
                                    BindingFlags.Instance);
   WriteIndent();
   Write(prefix);
   bool propWritten = false;
   foreach (MemberInfo m in members) {
     FieldInfo f = m as FieldInfo;
     PropertyInfo p = m as PropertyInfo;
     if (f != null || p != null) {
       if (propWritten) {
         WriteTab();
       }
     else {
       propWritten = true;
     }
     Write(m.Name);
     Write("=");
     Type t = f != null ? f.FieldType : p.PropertyType;
     if (t.IsValueType || t == typeof(string)) {
       WriteValue(f != null ? f.GetValue(o) : p.GetValue(o, null));
     }
     else {
       if (typeof(IEnumerable).IsAssignableFrom(t)) {
         Write("...");
       }
       else {
         Write("{ }");
       }
     }
   }
 }
 if (propWritten) WriteLine();
   if (level < depth) {
     foreach (MemberInfo m in members) {

FieldInfo f = m as FieldInfo;
     PropertyInfo p = m as PropertyInfo;
     if (f != null || p != null) {
       Type t = f != null ? f.FieldType : p.PropertyType;
       if (!(t.IsValueType || t == typeof(string))) {
         object value =
           f != null ? f.GetValue(o) : p.GetValue(o, null);
         if (value != null) {
           level++;
           WriteObject(m.Name + ": ", value);
           level--;
         }
       }
     }
   }
  }
 }
}

The Write method makes an internal call to the WriteObject private method that is the real core of all the ObjectDumper class. In the first section of the code it checks if the object is null, a string, or an object representing a value type. In the case of a value type, an output is provided without other checks. Instead, when the parameter object o implements the IEnumerable<T> interface the method code goes through each element of the parameter in order to check if other elements implement IEnumerable<T>. If not, the object will be passed again to the same method, which will use .NET Reflection to get its value.

The query expression is evaluated in the foreach statement. This behavior is guaranteed by the yield keyword used in the methods (called standard query operators in LINQ; see the next section) defined in the System.Linq namespace. For an example, let's look at the Where<T> method body in Listing 1-9:

Example 1-9. The Body of the Where<T> Method
public static IEnumerable<T> Where<T>(
    this IEnumerable<T> source, Func<T, bool> predicate)
{
    if (source == null) throw Error.ArgumentNull("source");
    if (predicate == null)
      throw Error.ArgumentNull("predicate");
    return WhereIterator<T>(source, predicate);
}

static IEnumerable<T> WhereIterator<T>(
    IEnumerable<T> source, Func<T, bool> predicate)
{
    foreach (T element in source) {
        if (predicate(element)) yield return element;
    }
}

The Where<T> method calls the private WhereIterator<T> method after having checked that both arguments are not null. (WhereIterator<T> is not called if only one argument is null.) In the WhereIterator<T> method, the yield keyword is used to collect the items that satisfy the condition expressed with the predicate delegate function.

It's possible to cache the result of a query using the ToList and ToArray methods. Let's look at the example in Listing 1-10:

Example 1-10. The ToArray() Method in Action
List<Person> people = new List<Person> {
    new Person() { ID = 1,
                    IDRole = 1,
                    LastName = "Anderson",
                    FirstName = "Brad"},
    new Person() { ID = 2,
                    IDRole = 2,
                    LastName = "Gray",
                    FirstName = "Tom"}
            };

var query = people
            .Where (p => p.ID == 1)
            .Select(p => new { p.FirstName, p.LastName } )
            .ToArray();

ObjectDumper.Write(query);

people[0].FirstName = "Fabio";

ObjectDumper.Write(query);

In Listing 1-10 the code caches the result of a query using the ToArray method. As the output in Figure 1-4 shows, even if the code changes an element of the List<T> collection, the query returns the same result since it has been cached.

Figure 1-4. The output of the ToArray() example in Listing 1-10

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.115.154