QYYEE 5 lat temu
rodzic
commit
ea71fda902
1 zmienionych plików z 300 dodań i 1 usunięć
  1. 300
    1
      实践/后端/C#基础/21.泛型.md

+ 300
- 1
实践/后端/C#基础/21.泛型.md Wyświetl plik

@@ -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
+

Ładowanie…
Anuluj
Zapisz