|
@@ -24,4 +24,303 @@
|
24
|
24
|
public delegate void EventHandle<TEventArgs>(object sender,TEventArgs e);
|
25
|
25
|
public delegate TOutput Converter<TInput,TOutput>(TInput from);
|
26
|
26
|
public class SortedList<TKey,TValue>{}
|
27
|
|
- ```
|
|
27
|
+ ```
|
|
28
|
+
|
|
29
|
+> 创建泛型类
|
|
30
|
+- 在链表中,一个元素引用下一个元素。所以必须创建一个类,它将对象封装在链表中,并引用下一个对象。
|
|
31
|
+- 类LinkedListNode包含一个属性value,该属性用构造函数初始化。另外,LinkedListNode类包含对链表中下一个元素和上一个元素的引用,这些元素都可以从属性中访问。
|
|
32
|
+```
|
|
33
|
+public class LinkedListNode
|
|
34
|
+{
|
|
35
|
+ public LinkedListNode(object value)
|
|
36
|
+ {
|
|
37
|
+ Value=value;
|
|
38
|
+ }
|
|
39
|
+ public object Value{get;private set;}
|
|
40
|
+ public LinkedListNode Next{get;internal set;}
|
|
41
|
+ public LinkedListNode Prev{get;internal set;}
|
|
42
|
+}
|
|
43
|
+```
|
|
44
|
+- LinkedList类包含LinkedListNode类型的First和Last属性,它们分别标记了链表的头尾。AddLast()方法在链表尾添加一个新元素。首页创建一个LinkedListNode类型的对象。如果链表是空的,First和Last属性就设置为该新元素;否则,就把新元素添加为链表中的最后一个元素。通过实现GetEnumerator()方法,可以用foreach语句遍历链表。GetEnumerator()方法使用yield语句创建一个枚举器类型。
|
|
45
|
+```
|
|
46
|
+public class LinkedList:IEnumerable
|
|
47
|
+{
|
|
48
|
+ public LinkedListNode First{get;private set;}
|
|
49
|
+ public LinkedListNode Last{get;private set;}
|
|
50
|
+
|
|
51
|
+ public LinkedListNode AddLast(object node)
|
|
52
|
+ {
|
|
53
|
+ var newNode=new LinkedListNode(node);
|
|
54
|
+ if(First==null)
|
|
55
|
+ {
|
|
56
|
+ First=newNode;
|
|
57
|
+ Last=First;
|
|
58
|
+ }
|
|
59
|
+ else
|
|
60
|
+ {
|
|
61
|
+ LinkedListNode previous=Last;
|
|
62
|
+ Last.Next=newNode;
|
|
63
|
+ Last=newNode;
|
|
64
|
+ Last.Prev=previous;
|
|
65
|
+ }
|
|
66
|
+ return newNode;
|
|
67
|
+ }
|
|
68
|
+ public IEnumerator GrtEnumerator()
|
|
69
|
+ {
|
|
70
|
+ LinkedListNode current =First;
|
|
71
|
+ while(current!=null)
|
|
72
|
+ {
|
|
73
|
+ yield return current.Value;
|
|
74
|
+ current=current.Next;
|
|
75
|
+ }
|
|
76
|
+ }
|
|
77
|
+
|
|
78
|
+}
|
|
79
|
+```
|
|
80
|
+> 创建链表的泛型版本
|
|
81
|
+- LinkedListNode 类用一个泛型类型T声明。属性Value的类型是T,而不是object。构造函数也变为可以接受T类型的对象。也可以返回和设置泛型类型,所以属性Next和Prev的类型是LinkedLisTNode<T>
|
|
82
|
+```
|
|
83
|
+public class LinkedListNode<T>
|
|
84
|
+{
|
|
85
|
+ public LinkedListNode(T value)
|
|
86
|
+ {
|
|
87
|
+ Value=value;
|
|
88
|
+ }
|
|
89
|
+ public T Value{get;private set;}
|
|
90
|
+ public LinkedListNode<T> Next{get;internal set;}
|
|
91
|
+ public LinkedListNode<T> Prev{get;internal set;}
|
|
92
|
+}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+public class LinkedList<T>:IEnumerable<T>
|
|
96
|
+{
|
|
97
|
+ public LinkedListNode<T>Frist{get;private set;}
|
|
98
|
+ public LinkedListNode<T>Last{get;private set;}
|
|
99
|
+
|
|
100
|
+ public LinkedListNOde<T>AddLast(T node)
|
|
101
|
+ {
|
|
102
|
+ var newNode=new LinkedListNode<T>(node);
|
|
103
|
+ if(First==null)
|
|
104
|
+ {
|
|
105
|
+ First=newNode;
|
|
106
|
+ Last=First;
|
|
107
|
+ }
|
|
108
|
+ else
|
|
109
|
+ {
|
|
110
|
+ LinkedListNode<T>previous=Last;
|
|
111
|
+ LAst.Next=newNode;
|
|
112
|
+ Lsat=newNode;
|
|
113
|
+ Last.Prev=previous;
|
|
114
|
+ }
|
|
115
|
+ return newNode;
|
|
116
|
+ }
|
|
117
|
+
|
|
118
|
+ public IEnumerator<T> GetEnumerator()
|
|
119
|
+ {
|
|
120
|
+ LinkedListNode<T>current=First;
|
|
121
|
+ while(current!=null)
|
|
122
|
+ {
|
|
123
|
+ yield return current.Value;
|
|
124
|
+ current=current.Next;
|
|
125
|
+ }
|
|
126
|
+ }
|
|
127
|
+ IEnumerator IEnumerable.GetEnumerator()=>GetEnumerator();
|
|
128
|
+}
|
|
129
|
+```
|
|
130
|
+- 使用泛型LinkedList<T>,可以用int类型实例化它,且无需装箱操作。如果不使用AddLast()去传递int,就会出现一个编译器错误。使用泛型IEnumerable<T>,foreach语句也是类型安全的,如果foreach语句中的变量不是int,就会出现一个编译器错误
|
|
131
|
+```
|
|
132
|
+var list2=new LinkedList<int>();
|
|
133
|
+list2.AddLast(1);
|
|
134
|
+list2.AddLAst(3);
|
|
135
|
+list2.AddLAst(5);
|
|
136
|
+
|
|
137
|
+foreach(int i in list2)
|
|
138
|
+{
|
|
139
|
+ WriteLine(i);
|
|
140
|
+}
|
|
141
|
+对于字符串类型使用泛型LinkList<T>,将字符串传递给AddLast()方法。
|
|
142
|
+var list3=new LinkedList<string>();
|
|
143
|
+list3.AddLast("2");
|
|
144
|
+list3.AddLast("four");
|
|
145
|
+list3.AddLast("foo");
|
|
146
|
+
|
|
147
|
+foreach(string s in list3)
|
|
148
|
+{
|
|
149
|
+ WriteLine(s);
|
|
150
|
+}
|
|
151
|
+```
|
|
152
|
+- 每个处理对象类型的类都可以有泛型实现方式,另外,如果类使用了层次结构,泛型就非常有助于消除类型强制转换操作
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+> 泛型类的功能
|
|
156
|
+- 默认值
|
|
157
|
+- 约束
|
|
158
|
+- 继承
|
|
159
|
+- 静态成员
|
|
160
|
+
|
|
161
|
+**默认值**
|
|
162
|
+- 给DocumentManager<T>类添加一个GetDocument()方法。在这个方法中,应把类型T指定为null。但是,不能把null赋予泛型类型。原因是泛型类型也可以实例化值类型,而null只能用于引用类型。为了解决这个问题,可以使用default关键字。通过default关键字,将null赋予引用类型,将0赋予值类型。
|
|
163
|
+```
|
|
164
|
+public T GetDocument()
|
|
165
|
+{
|
|
166
|
+ T doc =default(T);
|
|
167
|
+ lock(this)
|
|
168
|
+ {
|
|
169
|
+ doc=documentQueue.Dequeue();
|
|
170
|
+ }
|
|
171
|
+ return doc;
|
|
172
|
+}
|
|
173
|
+```
|
|
174
|
+
|
|
175
|
+**约束**
|
|
176
|
+- 如果泛型类需要调用泛型类型中的方法,就必须添加约束
|
|
177
|
+- 对于DocumentManager<T>,文档的所有标题应在DisplayAllDocuments()方法中显示。Document类实现带有Title和Content属性的IDocument接口
|
|
178
|
+```
|
|
179
|
+public interface IDocument
|
|
180
|
+{
|
|
181
|
+ string Title{get;set;}
|
|
182
|
+ string Content{get;set;}
|
|
183
|
+}
|
|
184
|
+public class Document:IDocument
|
|
185
|
+{
|
|
186
|
+ public Document()
|
|
187
|
+ {
|
|
188
|
+
|
|
189
|
+ }
|
|
190
|
+ public Document(string title,string content)
|
|
191
|
+ {
|
|
192
|
+ Title=title;
|
|
193
|
+ Content=content;
|
|
194
|
+ }
|
|
195
|
+ public string Title{get;ste;}
|
|
196
|
+ public string Content{get;set;}
|
|
197
|
+}
|
|
198
|
+- 要使用DocumentManager<T>类显示文档,可以将类型T强制转换为IDocument接口,以显示标题
|
|
199
|
+
|
|
200
|
+public void DisplayAllDocuments()
|
|
201
|
+{
|
|
202
|
+ foreach(T doc in documentQueue)
|
|
203
|
+ {
|
|
204
|
+ WriteLine(((IDocument)doc).Title);
|
|
205
|
+ }
|
|
206
|
+}
|
|
207
|
+```
|
|
208
|
+- 如果类型T没有实现IDocument接口,这个类型强制转换就会导致一个运行时异常。最好给DocumentManager<TDocument>类定义一个约束:TDocument类型必须实现IDocument接口。
|
|
209
|
+- 为了在泛型类型的名称中制定该要求,将T改为TDocument。where子句指定了实现IDocument接口的需求
|
|
210
|
+```
|
|
211
|
+public class DocumentManager<TDocument> where TDocument:IDocument
|
|
212
|
+{
|
|
213
|
+ public void DisplayAllDocuments()
|
|
214
|
+ {
|
|
215
|
+ foreach (TDocument doc in documentQueue)
|
|
216
|
+ {
|
|
217
|
+ WriteLine(doc.Title);
|
|
218
|
+ }
|
|
219
|
+ }
|
|
220
|
+}
|
|
221
|
+```
|
|
222
|
+- 在main()方法中,用Document类型实例化DocumentManager<TDocument>类,而Document类型实现了需要的IDocument接口。接着添加和显示新文档,检索其中一个文档
|
|
223
|
+```
|
|
224
|
+public static void Main()
|
|
225
|
+{
|
|
226
|
+ var dm=new DocumentManager<Document>();
|
|
227
|
+ dm.AddDocument(new Document("Title A","Sample A"));
|
|
228
|
+ dm.AddDocument(new Document("Title B","Sample B"));
|
|
229
|
+
|
|
230
|
+ dm.DisplayAllDocuments();
|
|
231
|
+
|
|
232
|
+ if(dm.IsDocumentAvailable)
|
|
233
|
+ {
|
|
234
|
+ Document d=dm.GetDocument();
|
|
235
|
+ WriteLine(d.Content);
|
|
236
|
+ }
|
|
237
|
+}
|
|
238
|
+DocumentMAnager现在可以处理任何实现了IDocument接口的类。
|
|
239
|
+```
|
|
240
|
+- where T:struct :对于结构约束,类型T必须是值类型
|
|
241
|
+- where T:class :类约束指定类型T必须是引用类型
|
|
242
|
+- where T:IFoo :指定类型T必须实现接口IFoo
|
|
243
|
+- where T:Foo :指定类型T必须派生自基类Foo
|
|
244
|
+- where T:new() :这是一个构造函数约束,指定类型T必须有一个默认构造函数
|
|
245
|
+- where T1:T2 :这个约束也可以指定,类型T1派生自泛型类型T2
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+**继承**
|
|
250
|
+前面创建的LinkedList<T>类实现了IEnumerable<T>接口
|
|
251
|
+```
|
|
252
|
+public class LinkedList<T>:IEnumerable<T>
|
|
253
|
+{
|
|
254
|
+ //泛型类型可以实现泛型接口,也可以派生自一个类。泛型类可以派生自泛型基类
|
|
255
|
+}
|
|
256
|
+public class Base<T>
|
|
257
|
+{
|
|
258
|
+
|
|
259
|
+}
|
|
260
|
+
|
|
261
|
+public class Derived<T>:Base<T>
|
|
262
|
+{
|
|
263
|
+
|
|
264
|
+}
|
|
265
|
+其要求是必须重复接口的泛型类型,或者必须指定基类的类型
|
|
266
|
+
|
|
267
|
+public class Base<T>
|
|
268
|
+{
|
|
269
|
+
|
|
270
|
+}
|
|
271
|
+public class Derived<T>:Base<string>
|
|
272
|
+{}
|
|
273
|
+```
|
|
274
|
+- 派生类可以是泛型类或非泛型类。例如,可以定义一个抽象的泛型基类,它在派生类中用一个具体的类实现。这允许对特定类型执行特殊的操作
|
|
275
|
+```
|
|
276
|
+public abstract class Calc<T>
|
|
277
|
+{
|
|
278
|
+ public abstract T Add(T x,T y);
|
|
279
|
+ public abstract T Sub(T x,T y);
|
|
280
|
+}
|
|
281
|
+public class IntCalc:Calc<int>
|
|
282
|
+{
|
|
283
|
+ public override int Add(int x,int y)=>x+y;
|
|
284
|
+ public override int Sub(int x,int y)=>x-y;
|
|
285
|
+}
|
|
286
|
+```
|
|
287
|
+还可以创建一个部分的特殊操作,如从Query中派生StringQuery类,只定义一个泛型参数,如字符串TResult。要实例化StringQuery,只需要提供TRequest
|
|
288
|
+```
|
|
289
|
+public class Query<TRequest,TResult>
|
|
290
|
+{}
|
|
291
|
+public StringQuery<TRequest>:Query<TRequest,string>
|
|
292
|
+{}
|
|
293
|
+```
|
|
294
|
+
|
|
295
|
+**静态成员**
|
|
296
|
+- 泛型类的静态成员需要特别关注。泛型类的静态成员只能在类的一个实例中共享。
|
|
297
|
+```
|
|
298
|
+public class StaticDemo<T>
|
|
299
|
+{
|
|
300
|
+ public static int x;
|
|
301
|
+}
|
|
302
|
+由于同时对一个string类型和一个int类型使用StaticDemo<T>le类,因此存在两组静态字段:
|
|
303
|
+StaticDemo<string>.x=4;
|
|
304
|
+StaticDemo<int>.x=5;
|
|
305
|
+WriteLine(StaticDemo<string>.x);
|
|
306
|
+```
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+> 泛型接口
|
|
310
|
+- 如果泛型类型用out关键字标注,泛型接口就是协变得。这也意味着返回类型只能是T。接口IIndex与类型T是协变得,并从一个只读索引器中返回这个类型
|
|
311
|
+```
|
|
312
|
+public interface IIndex<out T>
|
|
313
|
+{
|
|
314
|
+ T this[int index]{get;}
|
|
315
|
+ int Count{get;}
|
|
316
|
+}
|
|
317
|
+
|
|
318
|
+```
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
|