Added address book example
diff --git a/src/AddressBook/AddPerson.cs b/src/AddressBook/AddPerson.cs
new file mode 100644
index 0000000..2497cec
--- /dev/null
+++ b/src/AddressBook/AddPerson.cs
@@ -0,0 +1,89 @@
+
+using System;
+using System.IO;
+
+namespace Google.ProtocolBuffers.Examples.AddressBook
+{
+  class AddPerson {
+    
+    /// <summary>
+    /// Builds a person based on user input
+    /// </summary>
+    static Person PromptForAddress(TextReader input, TextWriter output) {
+      Person.Builder person = Person.CreateBuilder();
+
+      output.Write("Enter person ID: ");
+      person.Id = int.Parse(input.ReadLine());
+
+      output.Write("Enter name: ");
+      person.Name = input.ReadLine();
+
+      output.Write("Enter email address (blank for none): ");
+      string email = input.ReadLine();
+      if (email.Length > 0) {
+        person.Email = email;
+      }
+
+      while (true) {
+        output.Write("Enter a phone number (or leave blank to finish): ");
+        string number = input.ReadLine();
+        if (number.Length == 0) {
+          break;
+        }
+
+        Person.Types.PhoneNumber.Builder phoneNumber =
+            Person.Types.PhoneNumber.CreateBuilder().SetNumber(number);
+
+        output.Write("Is this a mobile, home, or work phone? ");
+        String type = input.ReadLine();
+        switch (type) {
+          case "mobile":
+            phoneNumber.Type = Person.Types.PhoneType.MOBILE;
+            break;
+          case "home":
+            phoneNumber.Type = Person.Types.PhoneType.HOME;
+            break;
+          case "work":
+            phoneNumber.Type = Person.Types.PhoneType.WORK;
+            break;
+          default:
+            output.Write("Unknown phone type. Using default.");
+            break;
+        }
+
+        person.AddPhone(phoneNumber);
+      }
+      return person.Build();
+    }
+
+    /// <summary>
+    /// Entry point - loads an existing addressbook or creates a new one,
+    /// then writes it back to the file.
+    /// </summary>
+    public static int Main(string[] args) {
+      if (args.Length != 1) {
+        Console.Error.WriteLine("Usage:  AddPerson ADDRESS_BOOK_FILE");
+        return -1;
+      }
+
+      AddressBook.Builder addressBook = AddressBook.CreateBuilder();
+
+      if (File.Exists(args[0])) {
+        using (Stream file = File.OpenRead(args[0])) {
+          addressBook.MergeFrom(file);
+        }
+      } else {
+        Console.WriteLine("{0}: File not found. Creating a new file.", args[0]);
+      }
+
+      // Add an address.
+      addressBook.AddPerson(PromptForAddress(Console.In, Console.Out));
+
+      // Write the new address book back to disk.
+      using (Stream output = File.OpenWrite(args[0])) {
+        addressBook.Build().WriteTo(output);
+      }
+      return 0;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/AddressBook/AddressBook.csproj b/src/AddressBook/AddressBook.csproj
new file mode 100644
index 0000000..010199c
--- /dev/null
+++ b/src/AddressBook/AddressBook.csproj
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="3.5" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup>
+    <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+    <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+    <ProductVersion>9.0.30729</ProductVersion>
+    <SchemaVersion>2.0</SchemaVersion>
+    <ProjectGuid>{A31F5FB2-4FF3-432A-B35B-5CD203606311}</ProjectGuid>
+    <OutputType>Exe</OutputType>
+    <AppDesignerFolder>Properties</AppDesignerFolder>
+    <RootNamespace>Google.ProtocolBuffers.Examples.AddressBook</RootNamespace>
+    <AssemblyName>AddressBook</AssemblyName>
+    <TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
+    <FileAlignment>512</FileAlignment>
+    <TargetFrameworkSubset>
+    </TargetFrameworkSubset>
+    <StartupObject>Google.ProtocolBuffers.Examples.AddressBook.Program</StartupObject>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+    <DebugSymbols>true</DebugSymbols>
+    <DebugType>full</DebugType>
+    <Optimize>false</Optimize>
+    <OutputPath>bin\Debug\</OutputPath>
+    <DefineConstants>DEBUG;TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+    <DebugType>pdbonly</DebugType>
+    <Optimize>true</Optimize>
+    <OutputPath>bin\Release\</OutputPath>
+    <DefineConstants>TRACE</DefineConstants>
+    <ErrorReport>prompt</ErrorReport>
+    <WarningLevel>4</WarningLevel>
+  </PropertyGroup>
+  <ItemGroup>
+    <Reference Include="System" />
+  </ItemGroup>
+  <ItemGroup>
+    <Compile Include="AddPerson.cs" />
+    <Compile Include="AddressBookProtos.cs" />
+    <Compile Include="ListPeople.cs" />
+    <Compile Include="Program.cs" />
+    <Compile Include="Properties\AssemblyInfo.cs" />
+  </ItemGroup>
+  <ItemGroup>
+    <ProjectReference Include="..\ProtocolBuffers\ProtocolBuffers.csproj">
+      <Project>{6908BDCE-D925-43F3-94AC-A531E6DF2591}</Project>
+      <Name>ProtocolBuffers</Name>
+    </ProjectReference>
+  </ItemGroup>
+  <ItemGroup>
+    <None Include="app.config" />
+  </ItemGroup>
+  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+  <!-- To modify your build process, add your task inside one of the targets below and uncomment it. 
+       Other similar extension points exist, see Microsoft.Common.targets.
+  <Target Name="BeforeBuild">
+  </Target>
+  <Target Name="AfterBuild">
+  </Target>
+  -->
+</Project>
\ No newline at end of file
diff --git a/src/AddressBook/AddressBookProtos.cs b/src/AddressBook/AddressBookProtos.cs
new file mode 100644
index 0000000..45645ea
--- /dev/null
+++ b/src/AddressBook/AddressBookProtos.cs
@@ -0,0 +1,858 @@
+// Generated by the protocol buffer compiler.  DO NOT EDIT!
+
+using pb = global::Google.ProtocolBuffers;
+using pbc = global::Google.ProtocolBuffers.Collections;
+using pbd = global::Google.ProtocolBuffers.Descriptors;
+using scg = global::System.Collections.Generic;
+namespace Google.ProtocolBuffers.Examples.AddressBook {
+  
+  public static partial class AddressBookProtos {
+  
+    #region Descriptor
+    public static pbd::FileDescriptor Descriptor {
+      get { return descriptor; }
+    }
+    private static readonly pbd::FileDescriptor descriptor = pbd::FileDescriptor.InternalBuildGeneratedFileFrom(
+        global::System.Convert.FromBase64String(
+        "Chp0dXRvcmlhbC9hZGRyZXNzYm9vay5wcm90bxIIdHV0b3JpYWwaJGdvb2ds" + 
+        "ZS9wcm90b2J1Zi9jc2hhcnBfb3B0aW9ucy5wcm90bxogZ29vZ2xlL3Byb3Rv" + 
+        "YnVmL2Rlc2NyaXB0b3IucHJvdG8i2gEKBlBlcnNvbhIMCgRuYW1lGAEgAigJ" + 
+        "EgoKAmlkGAIgAigFEg0KBWVtYWlsGAMgASgJEisKBXBob25lGAQgAygLMhwu" + 
+        "dHV0b3JpYWwuUGVyc29uLlBob25lTnVtYmVyGk0KC1Bob25lTnVtYmVyEg4K" + 
+        "Bm51bWJlchgBIAIoCRIuCgR0eXBlGAIgASgOMhoudHV0b3JpYWwuUGVyc29u" + 
+        "LlBob25lVHlwZToESE9NRSIrCglQaG9uZVR5cGUSCgoGTU9CSUxFEAASCAoE" + 
+        "SE9NRRABEggKBFdPUksQAiIvCgtBZGRyZXNzQm9vaxIgCgZwZXJzb24YASAD" + 
+        "KAsyEC50dXRvcmlhbC5QZXJzb25CRUgBwj5ACitHb29nbGUuUHJvdG9jb2xC" + 
+        "dWZmZXJzLkV4YW1wbGVzLkFkZHJlc3NCb29rEhFBZGRyZXNzQm9va1Byb3Rv" + 
+        "cw=="),
+        new pbd::FileDescriptor[] {
+          global::Google.ProtocolBuffers.DescriptorProtos.CSharpOptions.Descriptor, 
+          global::Google.ProtocolBuffers.DescriptorProtos.DescriptorProtoFile.Descriptor, 
+        });
+    #endregion
+    
+    #region Static variables
+    internal static readonly pbd::MessageDescriptor internal__static_tutorial_Person__Descriptor
+        = Descriptor.MessageTypes[0];
+    internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Examples.AddressBook.Person, global::Google.ProtocolBuffers.Examples.AddressBook.Person.Builder> internal__static_tutorial_Person__FieldAccessorTable
+        = new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Examples.AddressBook.Person, global::Google.ProtocolBuffers.Examples.AddressBook.Person.Builder>(internal__static_tutorial_Person__Descriptor,
+            new string[] { "Name", "Id", "Email", "Phone", });
+    internal static readonly pbd::MessageDescriptor internal__static_tutorial_Person_PhoneNumber__Descriptor
+        = internal__static_tutorial_Person__Descriptor.NestedTypes[0];
+    internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber, global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber.Builder> internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable
+        = new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber, global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber.Builder>(internal__static_tutorial_Person_PhoneNumber__Descriptor,
+            new string[] { "Number", "Type", });
+    internal static readonly pbd::MessageDescriptor internal__static_tutorial_AddressBook__Descriptor
+        = Descriptor.MessageTypes[1];
+    internal static pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Examples.AddressBook.AddressBook, global::Google.ProtocolBuffers.Examples.AddressBook.AddressBook.Builder> internal__static_tutorial_AddressBook__FieldAccessorTable
+        = new pb::FieldAccess.FieldAccessorTable<global::Google.ProtocolBuffers.Examples.AddressBook.AddressBook, global::Google.ProtocolBuffers.Examples.AddressBook.AddressBook.Builder>(internal__static_tutorial_AddressBook__Descriptor,
+            new string[] { "Person", });
+    #endregion
+  }
+  #region Messages
+  public sealed partial class Person : pb::GeneratedMessage<Person, Person.Builder> {
+    private static readonly Person defaultInstance = new Builder().BuildPartial();
+    public static Person DefaultInstance {
+      get { return defaultInstance; }
+    }
+    
+    public override Person DefaultInstanceForType {
+      get { return defaultInstance; }
+    }
+    
+    protected override Person ThisMessage {
+      get { return this; }
+    }
+    
+    public static pbd::MessageDescriptor Descriptor {
+      get { return global::Google.ProtocolBuffers.Examples.AddressBook.AddressBookProtos.internal__static_tutorial_Person__Descriptor; }
+    }
+    
+    protected override pb::FieldAccess.FieldAccessorTable<Person, Person.Builder> InternalFieldAccessors {
+      get { return global::Google.ProtocolBuffers.Examples.AddressBook.AddressBookProtos.internal__static_tutorial_Person__FieldAccessorTable; }
+    }
+    
+    #region Nested types
+    public static class Types {
+      public enum PhoneType {
+        MOBILE = 0,
+        HOME = 1,
+        WORK = 2,
+      }
+      
+      public sealed partial class PhoneNumber : pb::GeneratedMessage<PhoneNumber, PhoneNumber.Builder> {
+        private static readonly PhoneNumber defaultInstance = new Builder().BuildPartial();
+        public static PhoneNumber DefaultInstance {
+          get { return defaultInstance; }
+        }
+        
+        public override PhoneNumber DefaultInstanceForType {
+          get { return defaultInstance; }
+        }
+        
+        protected override PhoneNumber ThisMessage {
+          get { return this; }
+        }
+        
+        public static pbd::MessageDescriptor Descriptor {
+          get { return global::Google.ProtocolBuffers.Examples.AddressBook.AddressBookProtos.internal__static_tutorial_Person_PhoneNumber__Descriptor; }
+        }
+        
+        protected override pb::FieldAccess.FieldAccessorTable<PhoneNumber, PhoneNumber.Builder> InternalFieldAccessors {
+          get { return global::Google.ProtocolBuffers.Examples.AddressBook.AddressBookProtos.internal__static_tutorial_Person_PhoneNumber__FieldAccessorTable; }
+        }
+        
+        private bool hasNumber;
+        private string number_ = "";
+        public bool HasNumber {
+          get { return hasNumber; }
+        }
+        public string Number {
+          get { return number_; }
+        }
+        
+        private bool hasType;
+        private global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType type_ = global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType.HOME;
+        public bool HasType {
+          get { return hasType; }
+        }
+        public global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType Type {
+          get { return type_; }
+        }
+        
+        public override bool IsInitialized {
+          get {
+            if (!hasNumber) return false;
+            return true;
+          }
+        }
+        
+        public override void WriteTo(pb::CodedOutputStream output) {
+          if (HasNumber) {
+            output.WriteString(1, Number);
+          }
+          if (HasType) {
+            output.WriteEnum(2, (int) Type);
+          }
+          UnknownFields.WriteTo(output);
+        }
+        
+        private int memoizedSerializedSize = -1;
+        public override int SerializedSize {
+          get {
+            int size = memoizedSerializedSize;
+            if (size != -1) return size;
+            
+            size = 0;
+            if (HasNumber) {
+              size += pb::CodedOutputStream.ComputeStringSize(1, Number);
+            }
+            if (HasType) {
+              size += pb::CodedOutputStream.ComputeEnumSize(2, (int) Type);
+            }
+            size += UnknownFields.SerializedSize;
+            memoizedSerializedSize = size;
+            return size;
+          }
+        }
+        
+        public static PhoneNumber ParseFrom(pb::ByteString data) {
+          return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+        }
+        public static PhoneNumber ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+          return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+        }
+        public static PhoneNumber ParseFrom(byte[] data) {
+          return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+        }
+        public static PhoneNumber ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+          return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+        }
+        public static PhoneNumber ParseFrom(global::System.IO.Stream input) {
+          return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+        }
+        public static PhoneNumber ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+          return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+        }
+        public static PhoneNumber ParseFrom(pb::CodedInputStream input) {
+          return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+        }
+        public static PhoneNumber ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+          return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+        }
+        public static Builder CreateBuilder() { return new Builder(); }
+        public override Builder CreateBuilderForType() { return new Builder(); }
+        public static Builder CreateBuilder(PhoneNumber prototype) {
+          return (Builder) new Builder().MergeFrom(prototype);
+        }
+        
+        public sealed partial class Builder : pb::GeneratedBuilder<PhoneNumber, Builder> {
+          protected override Builder ThisBuilder {
+            get { return this; }
+          }
+          public Builder() {}
+          
+          PhoneNumber result = new PhoneNumber();
+          
+          protected override PhoneNumber MessageBeingBuilt {
+            get { return result; }
+          }
+          
+          public override Builder Clear() {
+            result = new PhoneNumber();
+            return this;
+          }
+          
+          public override Builder Clone() {
+            return new Builder().MergeFrom(result);
+          }
+          
+          public override pbd::MessageDescriptor DescriptorForType {
+            get { return PhoneNumber.Descriptor; }
+          }
+          
+          public override PhoneNumber DefaultInstanceForType {
+            get { return PhoneNumber.DefaultInstance; }
+          }
+          
+          public override PhoneNumber BuildPartial() {
+            PhoneNumber returnMe = result;
+            result = null;
+            return returnMe;
+          }
+          
+          public override Builder MergeFrom(pb::IMessage other) {
+            if (other is PhoneNumber) {
+              return MergeFrom((PhoneNumber) other);
+            } else {
+              base.MergeFrom(other);
+              return this;
+            }
+          }
+          
+          public override Builder MergeFrom(PhoneNumber other) {
+            if (other == PhoneNumber.DefaultInstance) return this;
+            if (other.HasNumber) {
+              Number = other.Number;
+            }
+            if (other.HasType) {
+              Type = other.Type;
+            }
+            this.MergeUnknownFields(other.UnknownFields);
+            return this;
+          }
+          
+          public override Builder MergeFrom(pb::CodedInputStream input) {
+            return MergeFrom(input, pb::ExtensionRegistry.Empty);
+          }
+          
+          public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+            pb::UnknownFieldSet.Builder unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+            while (true) {
+              uint tag = input.ReadTag();
+              switch (tag) {
+                case 0: {
+                  this.UnknownFields = unknownFields.Build();
+                  return this;
+                }
+                default: {
+                  if (!ParseUnknownField(input, unknownFields, extensionRegistry, tag)) {
+                    this.UnknownFields = unknownFields.Build();
+                    return this;
+                  }
+                  break;
+                }
+                case 10: {
+                  Number = input.ReadString();
+                  break;
+                }
+                case 16: {
+                  int rawValue = input.ReadEnum();
+                  if (!global::System.Enum.IsDefined(typeof(global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType), rawValue)) {
+                    unknownFields.MergeVarintField(2, (ulong) rawValue);
+                  } else {
+                    Type = (global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType) rawValue;
+                  }
+                  break;
+                }
+              }
+            }
+          }
+          
+          
+          public bool HasNumber {
+            get { return result.HasNumber; }
+          }
+          public string Number {
+            get { return result.Number; }
+            set { SetNumber(value); }
+          }
+          public Builder SetNumber(string value) {
+            result.hasNumber = true;
+            result.number_ = value;
+            return this;
+          }
+          public Builder ClearNumber() {
+            result.hasNumber = false;
+            result.number_ = "";
+            return this;
+          }
+          
+          public bool HasType {
+           get { return result.HasType; }
+          }
+          public global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType Type {
+            get { return result.Type; }
+            set { SetType(value); }
+          }
+          public Builder SetType(global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType value) {
+            result.hasType = true;
+            result.type_ = value;
+            return this;
+          }
+          public Builder ClearType() {
+            result.hasType = false;
+            result.type_ = global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneType.HOME;
+            return this;
+          }
+        }
+      }
+      
+    }
+    #endregion
+    
+    private bool hasName;
+    private string name_ = "";
+    public bool HasName {
+      get { return hasName; }
+    }
+    public string Name {
+      get { return name_; }
+    }
+    
+    private bool hasId;
+    private int id_ = 0;
+    public bool HasId {
+      get { return hasId; }
+    }
+    public int Id {
+      get { return id_; }
+    }
+    
+    private bool hasEmail;
+    private string email_ = "";
+    public bool HasEmail {
+      get { return hasEmail; }
+    }
+    public string Email {
+      get { return email_; }
+    }
+    
+    private pbc::PopsicleList<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber> phone_ = new pbc::PopsicleList<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber>();
+    public scg::IList<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber> PhoneList {
+      get { return phone_; }
+    }
+    public int PhoneCount {
+      get { return phone_.Count; }
+    }
+    public global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber GetPhone(int index) {
+      return phone_[index];
+    }
+    
+    public override bool IsInitialized {
+      get {
+        if (!hasName) return false;
+        if (!hasId) return false;
+        foreach (global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber element in PhoneList) {
+          if (!element.IsInitialized) return false;
+        }
+        return true;
+      }
+    }
+    
+    public override void WriteTo(pb::CodedOutputStream output) {
+      if (HasName) {
+        output.WriteString(1, Name);
+      }
+      if (HasId) {
+        output.WriteInt32(2, Id);
+      }
+      if (HasEmail) {
+        output.WriteString(3, Email);
+      }
+      foreach (global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber element in PhoneList) {
+        output.WriteMessage(4, element);
+      }
+      UnknownFields.WriteTo(output);
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public override int SerializedSize {
+      get {
+        int size = memoizedSerializedSize;
+        if (size != -1) return size;
+        
+        size = 0;
+        if (HasName) {
+          size += pb::CodedOutputStream.ComputeStringSize(1, Name);
+        }
+        if (HasId) {
+          size += pb::CodedOutputStream.ComputeInt32Size(2, Id);
+        }
+        if (HasEmail) {
+          size += pb::CodedOutputStream.ComputeStringSize(3, Email);
+        }
+        foreach (global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber element in PhoneList) {
+          size += pb::CodedOutputStream.ComputeMessageSize(4, element);
+        }
+        size += UnknownFields.SerializedSize;
+        memoizedSerializedSize = size;
+        return size;
+      }
+    }
+    
+    public static Person ParseFrom(pb::ByteString data) {
+      return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+    }
+    public static Person ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+    }
+    public static Person ParseFrom(byte[] data) {
+      return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+    }
+    public static Person ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+    }
+    public static Person ParseFrom(global::System.IO.Stream input) {
+      return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+    }
+    public static Person ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+    }
+    public static Person ParseFrom(pb::CodedInputStream input) {
+      return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+    }
+    public static Person ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+    }
+    public static Builder CreateBuilder() { return new Builder(); }
+    public override Builder CreateBuilderForType() { return new Builder(); }
+    public static Builder CreateBuilder(Person prototype) {
+      return (Builder) new Builder().MergeFrom(prototype);
+    }
+    
+    public sealed partial class Builder : pb::GeneratedBuilder<Person, Builder> {
+      protected override Builder ThisBuilder {
+        get { return this; }
+      }
+      public Builder() {}
+      
+      Person result = new Person();
+      
+      protected override Person MessageBeingBuilt {
+        get { return result; }
+      }
+      
+      public override Builder Clear() {
+        result = new Person();
+        return this;
+      }
+      
+      public override Builder Clone() {
+        return new Builder().MergeFrom(result);
+      }
+      
+      public override pbd::MessageDescriptor DescriptorForType {
+        get { return Person.Descriptor; }
+      }
+      
+      public override Person DefaultInstanceForType {
+        get { return Person.DefaultInstance; }
+      }
+      
+      public override Person BuildPartial() {
+        result.phone_.MakeReadOnly();
+        Person returnMe = result;
+        result = null;
+        return returnMe;
+      }
+      
+      public override Builder MergeFrom(pb::IMessage other) {
+        if (other is Person) {
+          return MergeFrom((Person) other);
+        } else {
+          base.MergeFrom(other);
+          return this;
+        }
+      }
+      
+      public override Builder MergeFrom(Person other) {
+        if (other == Person.DefaultInstance) return this;
+        if (other.HasName) {
+          Name = other.Name;
+        }
+        if (other.HasId) {
+          Id = other.Id;
+        }
+        if (other.HasEmail) {
+          Email = other.Email;
+        }
+        if (other.phone_.Count != 0) {
+          base.AddRange(other.phone_, result.phone_);
+        }
+        this.MergeUnknownFields(other.UnknownFields);
+        return this;
+      }
+      
+      public override Builder MergeFrom(pb::CodedInputStream input) {
+        return MergeFrom(input, pb::ExtensionRegistry.Empty);
+      }
+      
+      public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+        pb::UnknownFieldSet.Builder unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+        while (true) {
+          uint tag = input.ReadTag();
+          switch (tag) {
+            case 0: {
+              this.UnknownFields = unknownFields.Build();
+              return this;
+            }
+            default: {
+              if (!ParseUnknownField(input, unknownFields, extensionRegistry, tag)) {
+                this.UnknownFields = unknownFields.Build();
+                return this;
+              }
+              break;
+            }
+            case 10: {
+              Name = input.ReadString();
+              break;
+            }
+            case 16: {
+              Id = input.ReadInt32();
+              break;
+            }
+            case 26: {
+              Email = input.ReadString();
+              break;
+            }
+            case 34: {
+              global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber.Builder subBuilder = global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber.CreateBuilder();
+              input.ReadMessage(subBuilder, extensionRegistry);
+              AddPhone(subBuilder.BuildPartial());
+              break;
+            }
+          }
+        }
+      }
+      
+      
+      public bool HasName {
+        get { return result.HasName; }
+      }
+      public string Name {
+        get { return result.Name; }
+        set { SetName(value); }
+      }
+      public Builder SetName(string value) {
+        result.hasName = true;
+        result.name_ = value;
+        return this;
+      }
+      public Builder ClearName() {
+        result.hasName = false;
+        result.name_ = "";
+        return this;
+      }
+      
+      public bool HasId {
+        get { return result.HasId; }
+      }
+      public int Id {
+        get { return result.Id; }
+        set { SetId(value); }
+      }
+      public Builder SetId(int value) {
+        result.hasId = true;
+        result.id_ = value;
+        return this;
+      }
+      public Builder ClearId() {
+        result.hasId = false;
+        result.id_ = 0;
+        return this;
+      }
+      
+      public bool HasEmail {
+        get { return result.HasEmail; }
+      }
+      public string Email {
+        get { return result.Email; }
+        set { SetEmail(value); }
+      }
+      public Builder SetEmail(string value) {
+        result.hasEmail = true;
+        result.email_ = value;
+        return this;
+      }
+      public Builder ClearEmail() {
+        result.hasEmail = false;
+        result.email_ = "";
+        return this;
+      }
+      
+      public scg::IList<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber> PhoneList {
+        get { return result.phone_; }
+      }
+      public int PhoneCount {
+        get { return result.PhoneCount; }
+      }
+      public global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber GetPhone(int index) {
+        return result.GetPhone(index);
+      }
+      public Builder SetPhone(int index, global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber value) {
+        result.phone_[index] = value;
+        return this;
+      }
+      public Builder SetPhone(int index, global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber.Builder builderForValue) {
+        result.phone_[index] = builderForValue.Build();
+        return this;
+      }
+      public Builder AddPhone(global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber value) {
+        result.phone_.Add(value);
+        return this;
+      }
+      public Builder AddPhone(global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber.Builder builderForValue) {
+        result.phone_.Add(builderForValue.Build());
+        return this;
+      }
+      public Builder AddRangePhone(scg::IEnumerable<global::Google.ProtocolBuffers.Examples.AddressBook.Person.Types.PhoneNumber> values) {
+        base.AddRange(values, result.phone_);
+        return this;
+      }
+      public Builder ClearPhone() {
+        result.phone_.Clear();
+        return this;
+      }
+    }
+  }
+  
+  public sealed partial class AddressBook : pb::GeneratedMessage<AddressBook, AddressBook.Builder> {
+    private static readonly AddressBook defaultInstance = new Builder().BuildPartial();
+    public static AddressBook DefaultInstance {
+      get { return defaultInstance; }
+    }
+    
+    public override AddressBook DefaultInstanceForType {
+      get { return defaultInstance; }
+    }
+    
+    protected override AddressBook ThisMessage {
+      get { return this; }
+    }
+    
+    public static pbd::MessageDescriptor Descriptor {
+      get { return global::Google.ProtocolBuffers.Examples.AddressBook.AddressBookProtos.internal__static_tutorial_AddressBook__Descriptor; }
+    }
+    
+    protected override pb::FieldAccess.FieldAccessorTable<AddressBook, AddressBook.Builder> InternalFieldAccessors {
+      get { return global::Google.ProtocolBuffers.Examples.AddressBook.AddressBookProtos.internal__static_tutorial_AddressBook__FieldAccessorTable; }
+    }
+    
+    private pbc::PopsicleList<global::Google.ProtocolBuffers.Examples.AddressBook.Person> person_ = new pbc::PopsicleList<global::Google.ProtocolBuffers.Examples.AddressBook.Person>();
+    public scg::IList<global::Google.ProtocolBuffers.Examples.AddressBook.Person> PersonList {
+      get { return person_; }
+    }
+    public int PersonCount {
+      get { return person_.Count; }
+    }
+    public global::Google.ProtocolBuffers.Examples.AddressBook.Person GetPerson(int index) {
+      return person_[index];
+    }
+    
+    public override bool IsInitialized {
+      get {
+        foreach (global::Google.ProtocolBuffers.Examples.AddressBook.Person element in PersonList) {
+          if (!element.IsInitialized) return false;
+        }
+        return true;
+      }
+    }
+    
+    public override void WriteTo(pb::CodedOutputStream output) {
+      foreach (global::Google.ProtocolBuffers.Examples.AddressBook.Person element in PersonList) {
+        output.WriteMessage(1, element);
+      }
+      UnknownFields.WriteTo(output);
+    }
+    
+    private int memoizedSerializedSize = -1;
+    public override int SerializedSize {
+      get {
+        int size = memoizedSerializedSize;
+        if (size != -1) return size;
+        
+        size = 0;
+        foreach (global::Google.ProtocolBuffers.Examples.AddressBook.Person element in PersonList) {
+          size += pb::CodedOutputStream.ComputeMessageSize(1, element);
+        }
+        size += UnknownFields.SerializedSize;
+        memoizedSerializedSize = size;
+        return size;
+      }
+    }
+    
+    public static AddressBook ParseFrom(pb::ByteString data) {
+      return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+    }
+    public static AddressBook ParseFrom(pb::ByteString data, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+    }
+    public static AddressBook ParseFrom(byte[] data) {
+      return ((Builder) CreateBuilder().MergeFrom(data)).BuildParsed();
+    }
+    public static AddressBook ParseFrom(byte[] data, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(data, extensionRegistry)).BuildParsed();
+    }
+    public static AddressBook ParseFrom(global::System.IO.Stream input) {
+      return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+    }
+    public static AddressBook ParseFrom(global::System.IO.Stream input, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+    }
+    public static AddressBook ParseFrom(pb::CodedInputStream input) {
+      return ((Builder) CreateBuilder().MergeFrom(input)).BuildParsed();
+    }
+    public static AddressBook ParseFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+      return ((Builder) CreateBuilder().MergeFrom(input, extensionRegistry)).BuildParsed();
+    }
+    public static Builder CreateBuilder() { return new Builder(); }
+    public override Builder CreateBuilderForType() { return new Builder(); }
+    public static Builder CreateBuilder(AddressBook prototype) {
+      return (Builder) new Builder().MergeFrom(prototype);
+    }
+    
+    public sealed partial class Builder : pb::GeneratedBuilder<AddressBook, Builder> {
+      protected override Builder ThisBuilder {
+        get { return this; }
+      }
+      public Builder() {}
+      
+      AddressBook result = new AddressBook();
+      
+      protected override AddressBook MessageBeingBuilt {
+        get { return result; }
+      }
+      
+      public override Builder Clear() {
+        result = new AddressBook();
+        return this;
+      }
+      
+      public override Builder Clone() {
+        return new Builder().MergeFrom(result);
+      }
+      
+      public override pbd::MessageDescriptor DescriptorForType {
+        get { return AddressBook.Descriptor; }
+      }
+      
+      public override AddressBook DefaultInstanceForType {
+        get { return AddressBook.DefaultInstance; }
+      }
+      
+      public override AddressBook BuildPartial() {
+        result.person_.MakeReadOnly();
+        AddressBook returnMe = result;
+        result = null;
+        return returnMe;
+      }
+      
+      public override Builder MergeFrom(pb::IMessage other) {
+        if (other is AddressBook) {
+          return MergeFrom((AddressBook) other);
+        } else {
+          base.MergeFrom(other);
+          return this;
+        }
+      }
+      
+      public override Builder MergeFrom(AddressBook other) {
+        if (other == AddressBook.DefaultInstance) return this;
+        if (other.person_.Count != 0) {
+          base.AddRange(other.person_, result.person_);
+        }
+        this.MergeUnknownFields(other.UnknownFields);
+        return this;
+      }
+      
+      public override Builder MergeFrom(pb::CodedInputStream input) {
+        return MergeFrom(input, pb::ExtensionRegistry.Empty);
+      }
+      
+      public override Builder MergeFrom(pb::CodedInputStream input, pb::ExtensionRegistry extensionRegistry) {
+        pb::UnknownFieldSet.Builder unknownFields = pb::UnknownFieldSet.CreateBuilder(this.UnknownFields);
+        while (true) {
+          uint tag = input.ReadTag();
+          switch (tag) {
+            case 0: {
+              this.UnknownFields = unknownFields.Build();
+              return this;
+            }
+            default: {
+              if (!ParseUnknownField(input, unknownFields, extensionRegistry, tag)) {
+                this.UnknownFields = unknownFields.Build();
+                return this;
+              }
+              break;
+            }
+            case 10: {
+              global::Google.ProtocolBuffers.Examples.AddressBook.Person.Builder subBuilder = global::Google.ProtocolBuffers.Examples.AddressBook.Person.CreateBuilder();
+              input.ReadMessage(subBuilder, extensionRegistry);
+              AddPerson(subBuilder.BuildPartial());
+              break;
+            }
+          }
+        }
+      }
+      
+      
+      public scg::IList<global::Google.ProtocolBuffers.Examples.AddressBook.Person> PersonList {
+        get { return result.person_; }
+      }
+      public int PersonCount {
+        get { return result.PersonCount; }
+      }
+      public global::Google.ProtocolBuffers.Examples.AddressBook.Person GetPerson(int index) {
+        return result.GetPerson(index);
+      }
+      public Builder SetPerson(int index, global::Google.ProtocolBuffers.Examples.AddressBook.Person value) {
+        result.person_[index] = value;
+        return this;
+      }
+      public Builder SetPerson(int index, global::Google.ProtocolBuffers.Examples.AddressBook.Person.Builder builderForValue) {
+        result.person_[index] = builderForValue.Build();
+        return this;
+      }
+      public Builder AddPerson(global::Google.ProtocolBuffers.Examples.AddressBook.Person value) {
+        result.person_.Add(value);
+        return this;
+      }
+      public Builder AddPerson(global::Google.ProtocolBuffers.Examples.AddressBook.Person.Builder builderForValue) {
+        result.person_.Add(builderForValue.Build());
+        return this;
+      }
+      public Builder AddRangePerson(scg::IEnumerable<global::Google.ProtocolBuffers.Examples.AddressBook.Person> values) {
+        base.AddRange(values, result.person_);
+        return this;
+      }
+      public Builder ClearPerson() {
+        result.person_.Clear();
+        return this;
+      }
+    }
+  }
+  
+  #endregion
+  
+}
diff --git a/src/AddressBook/ListPeople.cs b/src/AddressBook/ListPeople.cs
new file mode 100644
index 0000000..b2a9a96
--- /dev/null
+++ b/src/AddressBook/ListPeople.cs
@@ -0,0 +1,57 @@
+
+using System;
+using System.IO;
+
+namespace Google.ProtocolBuffers.Examples.AddressBook {
+  class ListPeople {
+    /// <summary>
+    /// Iterates though all people in the AddressBook and prints info about them.
+    /// </summary>
+    static void Print(AddressBook addressBook) {
+      foreach (Person person in addressBook.PersonList) {
+        Console.WriteLine("Person ID: {0}", person.Id);
+        Console.WriteLine("  Name: {0}", person.Name);
+        if (person.HasEmail) {
+          Console.WriteLine("  E-mail address: {0}", person.Email);
+        }
+
+        foreach (Person.Types.PhoneNumber phoneNumber in person.PhoneList) {
+          switch (phoneNumber.Type) {
+            case Person.Types.PhoneType.MOBILE:
+              Console.Write("  Mobile phone #: ");
+              break;
+            case Person.Types.PhoneType.HOME:
+              Console.Write("  Home phone #: ");
+              break;
+            case Person.Types.PhoneType.WORK:
+              Console.Write("  Work phone #: ");
+              break;
+          }
+          Console.WriteLine(phoneNumber.Number);
+        }
+      }
+    }
+
+    /// <summary>
+    /// Entry point - loads the addressbook and then displays it.
+    /// </summary>
+    public static int Main(string[] args) {
+      if (args.Length != 1) {
+        Console.Error.WriteLine("Usage:  ListPeople ADDRESS_BOOK_FILE");
+        return 1;
+      }
+
+      if (!File.Exists(args[0])) {
+        Console.WriteLine("{0} doesn't exist. Add a person to create the file first.", args[0]);
+        return 0;
+      }
+
+      // Read the existing address book.
+      using (Stream stream = File.OpenRead(args[0])) {
+        AddressBook addressBook = AddressBook.ParseFrom(stream);
+        Print(addressBook);
+      }
+      return 0;
+    }
+  }
+}
diff --git a/src/AddressBook/Program.cs b/src/AddressBook/Program.cs
new file mode 100644
index 0000000..b3e10cb
--- /dev/null
+++ b/src/AddressBook/Program.cs
@@ -0,0 +1,55 @@
+using System;
+
+namespace Google.ProtocolBuffers.Examples.AddressBook
+{
+  /// <summary>
+  /// Entry point. Repeatedly prompts user for an action to take, delegating actual behaviour
+  /// to individual actions. Each action has its own Main method, so that it can be used as an
+  /// invidual complete program.
+  /// </summary>
+  class Program {
+    static int Main(string[] args) {
+      if (args.Length > 1) {
+        Console.Error.WriteLine("Usage: AddressBook [file]");
+        Console.Error.WriteLine("If the filename isn't specified, \"addressbook.data\" is used instead.");
+        return 1;
+      }
+      string addressBookFile = args.Length > 0 ? args[0] : "addressbook.data";
+
+      bool stopping = false;
+      while (!stopping) {
+        Console.WriteLine("Options:");
+        Console.WriteLine("  L: List contents");
+        Console.WriteLine("  A: Add new person");
+        Console.WriteLine("  Q: Quit");
+        Console.Write("Action? ");
+        Console.Out.Flush();
+        char choice = Console.ReadKey().KeyChar;
+        Console.WriteLine();
+        try {
+          switch (choice) {
+            case 'A':
+            case 'a':
+              AddPerson.Main(new string[] { addressBookFile });
+              break;
+            case 'L':
+            case 'l':
+              ListPeople.Main(new string[] { addressBookFile });
+              break;
+            case 'Q':
+            case 'q':
+              stopping = true;
+              break;
+            default:
+              Console.WriteLine("Unknown option: {0}", choice);
+              break;
+          }
+        } catch (Exception e) {
+          Console.WriteLine("Exception executing action: {0}", e);
+        }
+        Console.WriteLine();
+      } 
+      return 0;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/AddressBook/Properties/AssemblyInfo.cs b/src/AddressBook/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..aac45d5
--- /dev/null
+++ b/src/AddressBook/Properties/AssemblyInfo.cs
@@ -0,0 +1,35 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("AddressBook")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("AddressBook")]
+[assembly: AssemblyCopyright("Copyright ©  2008")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible 
+// to COM components.  If you need to access a type in this assembly from 
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("57123e6e-28d1-4b9e-80a5-5e720df8035a")]
+
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers 
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/AddressBook/app.config b/src/AddressBook/app.config
new file mode 100644
index 0000000..df20690
--- /dev/null
+++ b/src/AddressBook/app.config
@@ -0,0 +1,3 @@
+<?xml version="1.0"?>
+<configuration>
+<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>
diff --git a/src/ProtocolBuffers.sln b/src/ProtocolBuffers.sln
index c4f9877..c2cd0bf 100644
--- a/src/ProtocolBuffers.sln
+++ b/src/ProtocolBuffers.sln
@@ -9,6 +9,8 @@
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProtoGen.Test", "ProtoGen.Test\ProtoGen.Test.csproj", "{C268DA4C-4004-47DA-AF23-44C983281A68}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AddressBook", "AddressBook\AddressBook.csproj", "{A31F5FB2-4FF3-432A-B35B-5CD203606311}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -31,6 +33,10 @@
 		{C268DA4C-4004-47DA-AF23-44C983281A68}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{C268DA4C-4004-47DA-AF23-44C983281A68}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{C268DA4C-4004-47DA-AF23-44C983281A68}.Release|Any CPU.Build.0 = Release|Any CPU
+		{A31F5FB2-4FF3-432A-B35B-5CD203606311}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{A31F5FB2-4FF3-432A-B35B-5CD203606311}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{A31F5FB2-4FF3-432A-B35B-5CD203606311}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{A31F5FB2-4FF3-432A-B35B-5CD203606311}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE