瀏覽代碼

yield语句和运算符与类型强制转换

master
QYYEE 5 年之前
父節點
當前提交
36f79de0d2

+ 81
- 1
实践/后端/C#基础/21.泛型.md 查看文件

@@ -306,7 +306,7 @@ WriteLine(StaticDemo<string>.x);
306 306
 ```
307 307
 
308 308
 
309
-> 泛型接口
309
+> 泛型接口的协变
310 310
 - 如果泛型类型用out关键字标注,泛型接口就是协变得。这也意味着返回类型只能是T。接口IIndex与类型T是协变得,并从一个只读索引器中返回这个类型
311 311
 ```
312 312
 public interface IIndex<out T>
@@ -314,9 +314,89 @@ public interface IIndex<out T>
314 314
     T this[int index]{get;}
315 315
     int Count{get;}
316 316
 }
317
+IIndex<T>接口用RectangleCollection类来实现。RectangleCollection类为泛型类型T定义了Rectangle
318
+```
319
+
320
+>泛型接口的抗变
321
+- 如果泛型类型用in关键字标注,泛型接口就是抗变。这样,接口只能把泛型类型T用作其方法的输入
322
+```
323
+public interface IDisplay<in T>
324
+{
325
+    void Show(T item);
326
+}
327
+```
328
+- ShapeDisplay 类实现IDisplay<Shape>,并使用Shape对象作为输入参数
329
+```
330
+public class ShapeDisplay:IDisplay<Shape>
331
+{
332
+    public void Show(Shape s)=> WriteLine($"{s.GetType().Name}Width:{s.Width},Height:{s.Height}");
333
+}
334
+```
335
+
336
+> 泛型方法
337
+- 在泛型方法中,泛型类型用方法声明来定义。泛型方法可以在非泛型类中定义.
338
+- Swap<T>()方法把T定义为泛型类型,该泛型类型用于两个参数和一个变量temp:
339
+```
340
+void Swap<T>(ref T x,ref T y)
341
+{
342
+    T temp;
343
+    temp=x;
344
+    x=y;
345
+    y=temp;
346
+}
347
+把泛型类型赋予方法调用,就可以调用泛型方法:
348
+int i=4;
349
+int j=5;
350
+Swap<int>(ref i,ref j);
317 351
 
318 352
 ```
353
+但是,因为C#编译器会通过调用Swap()方法来获取参数的类型,所以不需要把泛型类型赋予方法调用。泛型方法可以像非泛型方法那样调用:
354
+```
355
+int i=4;
356
+int j=5;
357
+Swap(ref i,ref j)
358
+```
359
+> 泛型方法示例
360
+- 使用包含Name和Balance属性的Account类
361
+```
362
+public class Account
363
+{
364
+    public string Name{get;}
365
+    public decimal Balance{get;private set;}
366
+
367
+    public Account(string name,Decimal balance)
368
+    {
369
+        Name=name;
370
+        Balance=balance;
371
+    }
372
+}
373
+- 其中应累加余额的所有账户操作都添加到List<Account>类型的账户列表中
374
+var accounts=new List<ACcount>()
375
+{
376
+    new Account("Christian",1500),
377
+    new Account("Stephanie",2200),
378
+    new Account("Angela",1800),
379
+    new Account("Matthias",2400)
380
+};
381
+```
382
+- 累加所有Account对象的传统方式是用foreach语句遍历所有的Account对象。
383
+- 如下所示。foreach语句使用IEnumerable接口迭代集合的元素,所以AccumulateSimple()方法的参数是IEnumerable类型。foreach语句处理实现IEnumerable接口的每个对象。这样,AccumulateSimple()方法就可以用于所有实现IEnumerable<Account>接口的集合类。在这个方法的实现代码中,直接访问Account对象的Balance属性
384
+```
385
+public static class Algorithms
386
+{
387
+    public static decimal AccumulateSimple(IEnumrable<Account>source)
388
+    {
389
+        decimal sum=0;
390
+        foreach(Account a in source)
391
+        {
392
+            sum+=a.Balance;
393
+        }
394
+        return sum;
395
+    }
396
+}
319 397
 
398
+decimal amount =Algorithms.AccumulateSimple(accounts);
399
+``` 
320 400
 
321 401
 
322 402
 

+ 113
- 0
实践/后端/C#基础/22.yield语句.md 查看文件

@@ -0,0 +1,113 @@
1
+**用yield return 语句实现一个简单结合**
2
+- HelloCollection 类包括GetEnumerator()方法。该方法的实现代码包含两条yield return语句,它们分别返回字符串Hello和World
3
+```
4
+using System;
5
+using System.Collections;
6
+
7
+namepace Wrox.ProCSharp.Arrays
8
+{
9
+    public class HelloCollection
10
+    {
11
+        public IEnumerator<string>GetEnumerator()
12
+        {
13
+            yield return"HEllo";
14
+            yield return "World";
15
+        }
16
+    }
17
+}
18
+现在可以用foreach语句迭代集合
19
+public void HelloWord()
20
+{
21
+    var helloCollection=new HelloCollection();
22
+    foreach(var s in helloCollection)
23
+    {
24
+        WriteLine(s);
25
+    }
26
+}
27
+```
28
+-包含yield 语句的方法或属性也称为迭代块。迭代块必须声明为返回IEnumerator或IEnumerable接口,或者这些接口的泛型版本,这个块可以包含多条yield return语句或yield break 语句,但不能包含return语句。
29
+- 下面例子,可以把yield类型看做内部类Enumerator。外部类的GEtEnumerator()方法实例化并返回一个新的yield类型。在yield类型中,变量state定义了迭代的当前位置,每次调用MoveNext()时,当前位置都会改变。MoveNext()封装了迭代块的代码,并设置了current变量的值,从此使Current属性根据位置返回一个对象。
30
+```
31
+public class HelloCollection
32
+{
33
+    public IEnumerator GetEnumerator()=>new Enumerator(0);
34
+    public class Enumerator :IEnumerator<string>,IEnumerator,IDisposable
35
+    {
36
+        private int_state;
37
+        private string _current;
38
+        public Enumerator(int state)
39
+        {
40
+            _state =state;
41
+        }
42
+        bool System.Collections.IEnumerator.MoveNext()
43
+        {
44
+            switch(state)
45
+            {
46
+                case 0:
47
+                _current="Hello";
48
+                _state=1;
49
+                return true;
50
+                case 1:
51
+                _current="World";
52
+                _state=2;
53
+                return true;
54
+                case 2:
55
+                break;
56
+
57
+            }
58
+            return false;
59
+        }
60
+        void System.Collections.IEnumerator.Reset()
61
+        {
62
+            throw new NotSupportedException();
63
+        }
64
+        string System.Collections.Generic.IEnumerator<string>.Current=>current;
65
+
66
+        void IDisposable.Dispose()
67
+        {}
68
+
69
+    }
70
+}
71
+```
72
+> 用yield return 返回枚举器
73
+- 例子说明:在Tic-Tac-Toe 游戏中有9个域,玩家轮流在这些域中放置一个“十”字或一个圆。这些移动操作由GameMoves类模拟。方法Cross()和Circle()是创建迭代类型的迭代块。在Cross()迭代块中,将移动操作的信息写到控制台上,并递增移动次数。如果移动次数大于8,就用yield break停止迭代;否则,就在每次迭代中返回yield类型circle的枚举对象。Circle()迭代块非常类似于Cross()迭代块,知识它在每次迭代中返回cross迭代器类型
74
+```
75
+public class GameMoves
76
+{
77
+    private IEnumerator _cross;
78
+    private IEnumerator _circle;
79
+
80
+    public GameMoves()
81
+    {
82
+      _cross=Cross();
83
+      _circle=Circle();  
84
+    }
85
+    private int _move=0;
86
+    const int MAxMoves=9;
87
+
88
+    public IEnumerator Cross()
89
+    {
90
+        while (true)
91
+        {
92
+            WriteLine($"Cross,move {_move}");
93
+            if(++_move >=MaxMoves)
94
+            {
95
+                yield break;
96
+            }
97
+            yield return _circle;
98
+        }
99
+    }
100
+    public IEnumerator Circle()
101
+    {
102
+        while(true)
103
+        {
104
+            WriteLine($"Circle,move{move}");
105
+            if(++_move>=MaxMoves)
106
+            {
107
+                yield break;
108
+            }
109
+            yield return _cross;
110
+        }
111
+    }
112
+}
113
+```

+ 77
- 0
实践/后端/C#基础/23.运算符与类型强制转换.md 查看文件

@@ -0,0 +1,77 @@
1
+> 条件运算符
2
+- 条件运算符(?:)也称为三元运算符,是if...else结构的简化形式。其名称的出处是它带有3个操作数。它首先判断一个条件,如果条件为真,就返回一个值;如果条件为假,则返回另一个值
3
+- 语法:condition?true_value:false_value
4
+- 其中condition是要判断的布尔表达式,true_value是condition为真时返回的值,false_value是condition为假时返回的值。
5
+- 恰当地使用三元运算符,可以使程序非常简洁。它特别适合于给调用的函数提供两个参数中的一个。使用它可以把布尔值快速转换为字符串值true或false。它也很适合于显示正确的单数形式或复数形式,例如:
6
+```
7
+int x=1;
8
+string s=x+"";
9
+s+=(x==1?"man":"men");
10
+WriteLine(s);
11
+如果x等于1,这段代码就显示1 man:如果x等于其他数,就显示其正确的复数形式
12
+```
13
+
14
+> checked和unchecked运算符
15
+- 如果把一个代码块标记为checked,CLR就会执行溢出检查,如果发生溢出,就抛出OverflowException异常
16
+```
17
+byte b=255;
18
+checked
19
+{
20
+    b++;
21
+}
22
+WriteLine(b);
23
+运行改代码,会得到错误信息:
24
+system.OverflowException:Arithmetic operation resultes in an overflow.
25
+
26
+```
27
+- 用/checked 编译器选项进行编译,就可以检查程序中所有未标记代码的溢出。
28
+
29
+- 如果要禁止溢出检查,则可以把代码标记为unchecked:
30
+```
31
+byte b=255;
32
+unchecked
33
+{
34
+    b++;
35
+}
36
+WriteLine(b);
37
+```
38
+
39
+> is运算符
40
+- is运算符可以检查对象是否与特定的类型兼容。短语“兼容”表示对象或者是该类型,或者派生自该类型。
41
+- 例如,要检查变量是否与object类型兼容,代码如下
42
+```
43
+int i=10;
44
+if(i is object)
45
+{
46
+    WriteLine("i is an object");
47
+}
48
+```
49
+
50
+> as 运算符
51
+- as 运算符用于执行引用类型的显示类型转换。如果要转换的类型与指定的类型兼容,转换就会成功进行;如果类型不兼容,as运算符就会返回null值。
52
+- 下面的代码,如果object引用实际上不引用string实例,把object引用转换为string就会返回null:
53
+```
54
+object o1="Some String";
55
+object o2=5;
56
+string s1=o1 as string;//s1="Some String"
57
+string s2=o2 as string;//s2=null
58
+
59
+```
60
+- as 运算符允许在一步中进行安全的类型转换,不需要先试用is运算符测试类型,再执行转换。
61
+
62
+
63
+**sizeof运算符**
64
+- 使用sizeof运算符可以确定栈中值类型需要的长度
65
+
66
+
67
+**typeof运算符**
68
+- typeof运算符返回一个表示特定类型的System.Typed对象。在使用反射技术动态地查找对象的相关信息时,这个运算符很有用。
69
+
70
+
71
+**nameof运算符**
72
+- 该运算符接受一个符号、属性或方法,并返回其名称。
73
+
74
+
75
+**可空类型和运算符**
76
+- 值类型和引用类型的一个重要区别是,引用类型可以为空。值类型(如int)不能为空。
77
+

Loading…
取消
儲存