Project

General

Profile

root / branches / compiler / cSharp / ooasCompiler / src / libs / c5 / UserGuideExamples / IndexedObjects2.cs @ 3

1
/*
2
 Copyright (c) 2003-2007 Niels Kokholm and Peter Sestoft
3
*/
4

    
5
// IndexedObjects2.cs sketch 2007-07-28
6

    
7
// Maintaining multiple type safe indices on objects using the C5
8
// library.
9

    
10
// Compile with 
11
//   csc /r:C5.dll IndexedObjects2.cs 
12

    
13
using System;                           // Console
14
using System.Text;                      // StringBuilder
15
using C5; 
16
using SCG = System.Collections.Generic;
17

    
18
namespace IndexedObjects {
19
  static class IndexedObjectsMain {
20
    static void Main(String[] args) {
21
      // Create some Person objects:
22
      new Person("Niels",   19470206);
23
      new Person("Lone",    19600810);
24
      new Person("Peter",   19620625);
25
      new Person("Carsten", 19640627);
26
      new Person("Hanne",   19641209);
27
      new Person("Dorte",   19660930);
28
      new Person("Dorte",   19610312);
29
      new Person("J?rgen",  19340930);
30
      new Person("Kirsten", 19360114);
31
      new Person("Henrik",  19360630);
32
      new Person("Lars",    19640625);
33
      new Person("Thora",   19091129);
34
      // Try the indexers:
35
      Console.WriteLine("\nBorn in 1964:");
36
      foreach (Person p in Person.Year[1964]) 
37
        Console.WriteLine(p);
38
      Console.WriteLine("\nNamed Dorte:");
39
      foreach (Person p in Person.Name["Dorte"]) 
40
        Console.WriteLine(p);
41
      Console.WriteLine("\nBorn on the 30th:");
42
      foreach (Person p in Person.Day[30]) 
43
        Console.WriteLine(p);
44
      Console.WriteLine("\nBorn June-October:");
45
      foreach (Person p in Person.Month.RangeFromTo(Month.Jun, Month.Nov)) 
46
        Console.WriteLine(p);
47
    }
48
  }
49

    
50
  // The interface IIndex<Q,T> describes an index with keys of type Q
51
  // on items of type T:
52

    
53
  public interface IIndex<Q,T> { 
54
    ICollection<T> this[Q x] { get; }
55
    void Add(T item);
56
  }
57

    
58
  // A hash-based index supports adding items of type T to the index
59
  // and looking up items by key of type Q.  The function toKey
60
  // transforms an item to its index key.
61

    
62
  // The hash-based index is implemented by a hash dictionary that
63
  // maps each index key to a hash set of items.  The hash dictionary
64
  // uses the key type's natural equality comparer.  The hash set uses
65
  // a reference equality comparer for the item type -- it might also
66
  // use the item type's natural comparer.
67

    
68
  public class HashIndex<Q,T> : IIndex<Q,T> where T : class {
69
    private readonly Fun<T,Q> toKey;
70
    private HashDictionary<Q, HashSet<T>> dict;
71

    
72
    public HashIndex(Fun<T,Q> toKey) {
73
      this.toKey = toKey;
74
      dict = new HashDictionary<Q, HashSet<T>>();
75
    }
76

    
77
    public void Add(T item) { 
78
      Q key = toKey(item);
79
      if (!dict.Contains(key))
80
        dict.Add(key, new HashSet<T>(ReferenceEqualityComparer<T>.Default));
81
      dict[key].Add(item);
82
    }
83

    
84
    public ICollection<T> this[Q key] {
85
      get { 
86
        return new GuardedCollection<T>(dict[key]);
87
      }
88
    }
89

    
90
    public override String ToString() {
91
      return dict.ToString();
92
    }
93
  }
94

    
95
  // A tree-based index supports adding items of type T to the index,
96
  // looking up items by key of type Q, and getting the items within a
97
  // key range.  The function toKey transforms an item to its index
98
  // key.  
99

    
100
  // The tree-based index is implemented by a tree dictionary that
101
  // maps each index key to a hash set of items.  The tree dictionary
102
  // uses the key type's natural comparer.  The hash set uses a
103
  // reference equality comparer for the item type -- it might also
104
  // use the item type's natural comparer.
105

    
106
  public class TreeIndex<Q,T> : IIndex<Q,T> where T : class {
107
    private readonly Fun<T,Q> toKey;
108
    private TreeDictionary<Q, HashSet<T>> dict;
109

    
110
    public TreeIndex(Fun<T,Q> toKey) {
111
      this.toKey = toKey;
112
      dict = new TreeDictionary<Q, HashSet<T>>();
113
    }
114

    
115
    public void Add(T item) { 
116
      Q key = toKey(item);
117
      if (!dict.Contains(key))
118
        dict.Add(key, new HashSet<T>(ReferenceEqualityComparer<T>.Default));
119
      dict[key].Add(item);
120
    }
121

    
122
    public ICollection<T> this[Q key] {
123
      get { 
124
        return new GuardedCollection<T>(dict[key]);
125
      }
126
    }
127

    
128
    // Bad implementation from a performance point of view: it may be
129
    // costly to build the result collection and possibly only its
130
    // size is ever used:
131

    
132
    public ICollection<T> RangeFromTo(Q x, Q y) {
133
      HashSet<T> result = new HashSet<T>(ReferenceEqualityComparer<T>.Default);
134
      foreach (KeyValuePair<Q,HashSet<T>> kv in dict.RangeFromTo(x, y))
135
	result.AddAll(kv.Value);
136
      return result;
137
    }
138

    
139
    public override String ToString() {
140
      return dict.ToString();
141
    }
142
  }
143

    
144
  public enum Month { 
145
    Jan=1, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec 
146
  }
147

    
148
  // Sample class with two fields and four different indexes
149
  
150
  // All indexes are defined as static fields and are strongly typed.
151
  // The definitions would be considerably shorter and clearer in C#
152
  // 3.0 thanks to lambda notation and better type inference.
153

    
154
  // Note all code related to indexing can be kept in one place, due
155
  // to the use of an indexing delegate, except for the call
156
  // Index(this), which must appear at the end of every constructor.
157
  // By using reflection and a naming convention for the indexing
158
  // delegate, one might be able to avoid explicitly adding the
159
  // indexes to the event in the static constructor.  However, the
160
  // present setup can be implemented using a static aspect weaver
161
  // such as yiihaw (Johansen and Spangenberg 2007), avoiding the use
162
  // of runtime reflection.
163

    
164
  // For use with LINQ-to-C5, one can use runtime reflection to find
165
  // the indexes on a type, as the public static fields of type
166
  // HashIndex or TreeIndex or IIndex.
167

    
168
  public class Person {
169
    public readonly String name;
170
    public readonly int date; // YYYYMMDD as in 20070725
171

    
172
    // Defining the indexes
173
    public static readonly IIndex<String, Person> Name 
174
      = new HashIndex<String, Person>(delegate(Person p) { return p.name; });
175
    public static readonly IIndex<int, Person> Year 
176
      = new HashIndex<int, Person>(delegate(Person p) { return p.date/10000; });
177
    public static readonly IIndex<int, Person> Day 
178
      = new HashIndex<int, Person>(delegate(Person p) { return p.date%100; });
179
    public static readonly TreeIndex<Month, Person> Month 
180
      = new TreeIndex<Month, Person>
181
        (delegate(Person p) { return (Month)(p.date/100%100); });
182

    
183
    // Defining and initializing the indexing delegate
184
    private static event Act<Person> Index;
185
    static Person() { 
186
      Index += Name.Add;
187
      Index += Year.Add;
188
      Index += Day.Add;
189
      Index += Month.Add;
190
    }
191

    
192
    public Person(String name, int date) {
193
      this.name = name;
194
      this.date = date;
195
      Index(this);
196
    }
197

    
198
    public override String ToString() {
199
      return name + " (" + date + ")";
200
    }
201
  }
202
}