1
|
/*
|
2
|
Copyright (c) 2003-2007 Niels Kokholm and Peter Sestoft
|
3
|
*/
|
4
|
|
5
|
// IndexedObjects.cs sketch 2007-07-26
|
6
|
|
7
|
// Other approaches: Define an Indexed<T> class to which indexers can
|
8
|
// be attached. An object can add (or remove) itself to the indexers
|
9
|
// that are attached to the Indexed class at the time of addition (or
|
10
|
// removal).
|
11
|
|
12
|
// Maintaining multiple indices on objects, each index defined by a
|
13
|
// delegate.
|
14
|
|
15
|
// Compile with
|
16
|
// csc /r:C5.dll IndexedObjects.cs
|
17
|
|
18
|
using System; // Console
|
19
|
using System.Text; // StringBuilder
|
20
|
using C5;
|
21
|
using SCG = System.Collections.Generic;
|
22
|
|
23
|
namespace IndexedObjects {
|
24
|
static class IndexedObjectsMain {
|
25
|
static void Main(String[] args) {
|
26
|
Indexed<Person> persons
|
27
|
= new Indexed<Person>
|
28
|
(new IndexMaker<Person,String>
|
29
|
("name", delegate(Person p) { return p.name; }),
|
30
|
new IndexMaker<Person,int>
|
31
|
("year", delegate(Person p) { return p.date/10000; }),
|
32
|
new IndexMaker<Person,int>
|
33
|
("day", delegate(Person p) { return p.date%100; }),
|
34
|
new IndexMaker<Person,String>
|
35
|
("month", delegate(Person p) { return months[p.date/100%100-1]; })
|
36
|
);
|
37
|
persons.Add(new Person("Niels", 19470206));
|
38
|
persons.Add(new Person("Lone", 19600810));
|
39
|
persons.Add(new Person("Peter", 19620625));
|
40
|
persons.Add(new Person("Carsten", 19640627));
|
41
|
persons.Add(new Person("Hanne", 19641209));
|
42
|
persons.Add(new Person("Dorte", 19660930));
|
43
|
persons.Add(new Person("Dorte", 19610312));
|
44
|
persons.Add(new Person("J?rgen", 19340930));
|
45
|
persons.Add(new Person("Kirsten", 19360114));
|
46
|
persons.Add(new Person("Henrik", 19360630));
|
47
|
persons.Add(new Person("Lars", 19640625));
|
48
|
persons.Add(new Person("Thora", 19091129));
|
49
|
Console.WriteLine("Born in 1964:");
|
50
|
foreach (Person p in persons["year"][1964])
|
51
|
Console.WriteLine(p);
|
52
|
Console.WriteLine("Born in June:");
|
53
|
foreach (Person p in persons["month"]["Jun"])
|
54
|
Console.WriteLine(p);
|
55
|
Console.WriteLine("Named Dorte:");
|
56
|
foreach (Person p in persons["name"]["Dorte"])
|
57
|
Console.WriteLine(p);
|
58
|
Console.WriteLine(persons);
|
59
|
}
|
60
|
|
61
|
private static readonly String[] months = {
|
62
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
63
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
|
64
|
};
|
65
|
}
|
66
|
|
67
|
public interface IIndexer<Q,R> {
|
68
|
ICollectionValue<R> this[Q x] { get; }
|
69
|
}
|
70
|
|
71
|
// An index maker has a name, it supports adding and removing items
|
72
|
// from the index, and looking up items by key (here of type
|
73
|
// Object).
|
74
|
|
75
|
public abstract class IndexMaker<T> : IIndexer<Object, T> {
|
76
|
public readonly String name;
|
77
|
public abstract bool Add(T item);
|
78
|
public abstract bool Remove(T item);
|
79
|
public abstract ICollectionValue<T> this[Object key] { get; }
|
80
|
|
81
|
public IndexMaker(String name) {
|
82
|
this.name = name;
|
83
|
}
|
84
|
}
|
85
|
|
86
|
// The implementation of an index maker consists of a function to
|
87
|
// map an item of type T to the index key type Q, and a dictionary
|
88
|
// that maps an index key (of type Q) to a set of items (each of
|
89
|
// type T).
|
90
|
|
91
|
public class IndexMaker<T,Q> : IndexMaker<T>
|
92
|
where T : class
|
93
|
where Q : IComparable<Q>
|
94
|
{
|
95
|
private readonly Fun<T,Q> fun;
|
96
|
private TreeDictionary<Q, HashSet<T>> dict;
|
97
|
|
98
|
public IndexMaker(String name, Fun<T,Q> fun) : base(name) {
|
99
|
this.fun = fun;
|
100
|
dict = new TreeDictionary<Q, HashSet<T>>();
|
101
|
}
|
102
|
|
103
|
public override bool Add(T item) {
|
104
|
Q key = fun(item);
|
105
|
if (!dict.Contains(key))
|
106
|
dict.Add(key, new HashSet<T>(ReferenceEqualityComparer<T>.Default));
|
107
|
return dict[key].Add(item);
|
108
|
}
|
109
|
|
110
|
public override bool Remove(T item) {
|
111
|
Q key = fun(item);
|
112
|
if (!dict.Contains(key))
|
113
|
return false;
|
114
|
return dict[key].Remove(item);
|
115
|
}
|
116
|
|
117
|
public ICollectionValue<T> this[Q key] {
|
118
|
get {
|
119
|
return dict[key];
|
120
|
}
|
121
|
}
|
122
|
|
123
|
public override ICollectionValue<T> this[Object key] {
|
124
|
get {
|
125
|
return dict[(Q)key];
|
126
|
}
|
127
|
}
|
128
|
|
129
|
public override String ToString() {
|
130
|
return dict.ToString();
|
131
|
}
|
132
|
}
|
133
|
|
134
|
// Weakly typed implementation of multiple indexers on a class T.
|
135
|
|
136
|
// The implementation is an array of index makers, each consisting
|
137
|
// of the index's name and its implementation which supports adding
|
138
|
// and removing T objects, and looking up T objects by the key
|
139
|
// relevant for that index.
|
140
|
|
141
|
public class Indexed<T> where T : class {
|
142
|
IndexMaker<T>[] indexMakers;
|
143
|
|
144
|
public Indexed(params IndexMaker<T>[] indexMakers) {
|
145
|
this.indexMakers = indexMakers;
|
146
|
}
|
147
|
|
148
|
public bool Add(T item) {
|
149
|
bool result = false;
|
150
|
foreach (IndexMaker<T> indexMaker in indexMakers)
|
151
|
result |= indexMaker.Add(item);
|
152
|
return result;
|
153
|
}
|
154
|
|
155
|
public bool Remove(T item) {
|
156
|
bool result = false;
|
157
|
foreach (IndexMaker<T> indexMaker in indexMakers)
|
158
|
result |= indexMaker.Remove(item);
|
159
|
return result;
|
160
|
}
|
161
|
|
162
|
public IIndexer<Object, T> this[String name] {
|
163
|
get {
|
164
|
foreach (IndexMaker<T> indexMaker in indexMakers)
|
165
|
if (indexMaker.name == name)
|
166
|
return indexMaker;
|
167
|
throw new Exception("Unknown index");
|
168
|
}
|
169
|
}
|
170
|
|
171
|
// For debugging
|
172
|
|
173
|
public override String ToString() {
|
174
|
StringBuilder sb = new StringBuilder();
|
175
|
foreach (IndexMaker<T> indexMaker in indexMakers) {
|
176
|
sb.Append("\n----- ").Append(indexMaker.name).Append("-----\n");
|
177
|
sb.Append(indexMaker);
|
178
|
}
|
179
|
return sb.ToString();
|
180
|
}
|
181
|
}
|
182
|
|
183
|
// Sample class with two fields but many possible indexes
|
184
|
|
185
|
public class Person {
|
186
|
public readonly String name;
|
187
|
public readonly int date; // YYYYMMDD as in 20070725
|
188
|
|
189
|
public Person(String name, int date) {
|
190
|
this.name = name;
|
191
|
this.date = date;
|
192
|
}
|
193
|
|
194
|
public override String ToString() {
|
195
|
return name + " (" + date + ")";
|
196
|
}
|
197
|
}
|
198
|
|
199
|
// The interface of a strongly typed indexing of Person objects:
|
200
|
// (Not yet used)
|
201
|
|
202
|
public interface PersonIndexers {
|
203
|
IIndexer<String, Person> Name { get; }
|
204
|
IIndexer<int, Person> Year { get; }
|
205
|
IIndexer<int, Person> Day { get; }
|
206
|
IIndexer<String, Person> Month { get; }
|
207
|
}
|
208
|
}
|