Iterators and Nullable Types

In this sixth part of a ten-part series that covers C# in depth, we will wrap up our discussion of iterators and begin looking at nullable types. It is excerpted from chapter four of C# 3.0 in a Nutshell, Third Edition, A Desktop Quick Reference, written by Joseph Albahari and Ben Albahari (O'Reilly; ISBN: 0596527578). Copyright © 2007 O'Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O'Reilly Media.

Contributed by
Rating: 5 stars5 stars5 stars5 stars5 stars / 1
October 23, 2008
Rate this Article:
MEH MEH++


SEARCH ASP FREE
TOOLS YOU CAN USE

advertisement

Composing Sequences

Iterators are highly composable. We can extend our example, this time to output even Fibonacci numbers only:

  using System;
  using System.Collections.Generic;

  class Test
 
{
    static void Main()
    {
     
foreach (int fib in EvenNumbersOnly(Fibs(6)))
        Console.WriteLine(fib);
    }

    static IEnumerable<int>Fibs(int fibCount)
   
{
      for (int i = 0, prevFib = 1, curFib = 1; i < fibCount; i++)
      {
        yield return prevFib;
        int newFib = prevFib+curFib;
        prevFib = curFib;
        curFib = newFib;

      }
    }

    static IEnumerable<int>EvenNumbersOnly(IEnumerable<int>sequence)
    {
     
foreach(int x in sequence)
        if ((x % 2) == 0)
          yield return x;
    }
  }

Each element is not calculated until the last moment—when requested by aMoveNext() operation. Figure 4-1 shows the data requests and data output over time.


Figure 4-1.  Composing sequences

The composability of the iterator pattern is extremely useful in LINQ; we will discuss the subject again in Chapter 8.

Constructing an Enumerable Object

You can instantiate and populate an enumerable object in a single step. For example:

  using System.Collections.Generic;
  ...

  List<int>list = new List<int>{1, 2, 3};

The compiler translates this to the following:

  using System.Collections.Generic;
  ...

  List<int>list = new List<int>();
  list.Add (1);
  list.Add (2);
  list.Add (3);

This requires that the enumerable object implements theSystem.Collections.IEnumerableinterface, and that it has anAddmethod that takes a single argument.

Nullable Types

Null Basics

Reference types can represent a nonexistent value with a null reference. Value types, however, cannot ordinarily represent null values. For example:

  string s = null;     // OK, Reference Type
  int i = null;        // Compile Error, Value Type cannot be null

To represent null in a value type, you must use a special construct called a nullable type. A nullable type is denoted with a value type followed by the? symbol:

  int? i = null;                   // OK, Nullable Type
  Console.WriteLine (i == null);   // True

Nullable<T> struct

T? translates into System.Nullable<T>. Nullable<T> is a lightweight immutable structure, having only two fields, to represent Value and HasValue. The essence ofSystem.Nullable<T>is very simple:

  public struct Nullable<T> where T : struct
  {
    public T Value {get;}
    public bool HasValue {get;}
    public T GetValueOrDefault();
    public T GetValueOrDefault(T defaultValue);
    ...
 
}

The code:

  int? i = null;
  Console.WriteLine (i == null);          // true

translates to:

  Nullable<int>i = new Nullable<int>();
  Console.WriteLine (! i.HasValue);       // true

Attempting to retrieveValuewhenHasValueis false throws anInvalidOperationException.GetValueOrDefault()returnsValue ifHasValueis true; otherwise, it returnsnewT()or a specified custom default value.

The default value ofT?isnull.

Implicit and explicit nullable conversions

The conversion from T to T? is implicit, and from T? to T is explicit. For example:

  int? x = 5;       // implicit
  int y = (int)x;   // explicit

The explicit cast is directly equivalent to calling the nullable object’sValueproperty. Hence, anInvalidOperationExceptionis thrown ifHasValueis false.

Boxing and unboxing nullable values

When T? is boxed, the boxed value on the heap contains T, not T?. This optimization is possible because a boxed value is a reference type that can already express null.

Lifted Operators

The Nullable<T> struct does not define operators such as <, >, or even ==. Despite this, the following code compiles and executes correctly:

  int? x = 5;
  int? y = 10;
  bool b = x < y;   // true

This works because the compiler steals or “lifts” the less-than operator from the underlying value type. Semantically, it translates the preceding comparison expression into this:

  bool b = (x.HasValue && y.HasValue) ? (x.Value < y.Value) : false;

In other words, if bothxandyhave values, it compares viaint’s less-than operator; otherwise, it returnsfalse.

Operator lifting means you can implicitly useT’s operators onT?. You can define operators forT?in order to provide special-purpose null behavior, but in the vast majority of cases, it’s best to rely on the compiler automatically applying systematic nullable logic for you. Here are some examples:

  int? x = 5;
  int? y = null;

  // equality operator examples 
  Console.WriteLine(x == y);       // false
  Console.WriteLine(x == null);    // false
  Console.WriteLine(x == 5);       // true
  Console.WriteLine(y == null);    // true
  Console.WriteLine(y == 5);       // false
  Console.WriteLine(y != 5);       // true

  // relational operator examples
  Console.WriteLine(x < 6);        // true
  Console.WriteLine(y < 6);        // false
  Console.WriteLine(y > 6);        // false

  // all other operator examples
  Console.WriteLine(x + 5);        // 10
  Console.WriteLine(x + y);        // null (prints empty line)

The compiler performs null logic differently depending on the category of operator. The following sections explain these different rules.

Equality operators (== !=)

The equality operators work on the principle that the behavior for nullable types works exactly as it does for reference types. This means a nonnull value is not equal to a null value, but two null values are equal.

  bool a = x == y;   // translation:
  bool a = (x != null && y != null) ? (x.Value == y.Value) :
                    (x != null ^ y != null);
  // a is true

Relational operators (< <= >= >)

The relational operators work on the principle that it is meaningless to compare null operands. This means comparing a null value to either a null or nonnull value returns false.

false .The relational operators work on the principle that it is meaningless to compare null operands. This means comparing a null value to either a null or nonnull value returns false.

  bool b = x < y;   // translation:
  bool b = (x == null || y == null) ? false : (x.Value < y.Value);

  // b is false

All other operators (+ - * / % & | ^ << >> + ++ - -- ! ~)

These operators work on the principle to always return “I don’t know” (i.e., null) when fed any operands that are null. This means that if any operand is null, the result is also null. This pattern should be familiar to SQL users.

  int? c = x + y;   // translation:
  int? c = (x == null || y == null) ? null : (int?)(x.Value + y.Value);

  // c is null

Mixing nullable and nonnullable operators

You can mix and match nullable and nonnullable types (this works because there is an implicit conversion from T to T?):

T to T? ):You can mix and match nullable and nonnullable types (this works because there is an implicit conversion from T to T?):

  int? x = null;
  int y = 2;
  int? z = x + y; // equivalent to x + (int?)y

  // z is null

bool?

When supplied operands of type bool?, the & and | operators treat null as an unknown value. So,null | trueis true, because:

  1. If the unknown value is false, the result would be true.
  2. If the unknown value is true, the result would be true.

Similarly,null & falseis false. This behavior would be familiar to SQL users. The following example enumerates other combinations:

  bool? n = null;
  bool? f = false;
 
bool? t = true;
  Console.WriteLine (n | n);   // (null)
 
Console.WriteLine (n | f);   // (null)
 
Console.WriteLine (n | t);   // True
  Console.WriteLine (n & n);   // (null)
 
Console.WriteLine (n & f);   // False
  Console.WriteLine (n & t);   // (null)

Null Coalescing Operator

The ?? operator is the null coalescing operator, and it can be used with both nullable types and reference types. It says “If the operand is nonnull, give it to me; otherwise, give me a default value.” For example:

  int? x = null;
  int y = x ?? 5;     // y is 5

The?? operator is equivalent to callingGetValueOrDefaultwith an explicit default value.

Please check back next week for the continuation of this article.

blog comments powered by Disqus
C# ARTICLES

- Beginning C#
- ASP.NET RedirectPermanent Method using C# an...
- C Programming Language and UNIX Pioneer Pass...
- Using Facebook JavaScript SDK in ASP.NET wit...
- ASP.NET Export to Excel and Word using VB.NE...
- WAV and MP3 Streaming with ASP.Net and C#
- Game Programming using SDL: the File I/O API
- C# and Java Developer Jobs on the Rise
- The Future Evolution of C# and VB.NET
- C# If and Else-if Statements
- How To Use the C# String Replace Method
- 5 Ways to Parse XML in C#
- C# Meets Design Patterns
- Coding a CRC-Generating Algorithm in C
- Cyclic Redundancy Check

ASP Web Hosting ASP.Net Web Hosting Windows Web Hosting
 
 
 

ASP Free Forums 
 RSS  Tutorials RSS
 RSS  Forums RSS
 RSS  All Feeds
Site Map 
Request Media Kit
Write For Us Get Paid 
Weekly Newsletter
 
Developer Updates  
Free Website Content 
Privacy Policy 
Support 


© 2003-2012 by Developer Shed. All rights reserved. DS Cluster 10 - Follow our Sitemap
Most Popular Topics
All ASP.Net Tutorials