深入理解C#|02 C#2
泛型
略
可空值类型
对于一个值类型,他一定是有效的,但他的值不一定是有效的
举例来说,如果需要用户输入一个体重float,当用户没有输入时,float默认就会是0,但很明显,此时的体重是非法的,是应该报错的
解决办法有两个
- 当没有输入值时,我们用一个默认值代替,系统默认是0,我们可以设置成其他的
- 额外设置一个bool标志位,当为false时,代表数据非法
很明显这两种解决办法,多少都是有一些问题的,于是产生了可空值类型
Nullable<T>结构体
可空值类型是一种数据结构,早期核心代码如下
public struct Nullable<T> where T : struct
{
private readonly T value;
private readonly bool hasValue;
public Nullable(T value)
{
this.value = value;
this.hasValue = true;
}
public bool HasValue => hasValue;
public T Value
{
get
{
if(!hasValue)
{
throw new Exception();
}
return value;
}
}
}
由于结构体的特性,默认还会有一个隐藏的构造函数
value会是T的默认值,注意,虽然value是有值的(值类型必定有一个默认值),但是此时我们认为这个value是无效的,逻辑上是!hasValue
public struct Nullable<T> where T : struct
{
public Nullable()
{
this.value = default;
this.hasValue = false;
}
}
装箱操作
int x = 5;
object o = x;
此时o是对于装箱int的引用,但是在C#里,装箱int和int没有显示区别,o.GetType(),和typeof(int),结果是一样的
而可空值类型则并非如此
- 当hasValue时,结果是对于装箱T的引用
- 当!hasValue时,结果是对NULL的引用
语法
?后缀
以下两种声明是等价的
Nullable<int> x;
int? y;
null字面值
- 当null用于引用类型时,表示一个空的引用
- 当null用于可空值类型时,表示一个!hasValue的可空值类型
以下二者等价
int? x;
if (x != null){}
if (x.HasValue){}
提升运算符
对于非可控类型T,我们可以重载很多运算符,诸如:+、++、-、–等等
当我们对T进行运算符重载之后,Nullable<T>也会进行相同的操作,此时Nullable<T>里的运算符,称为提升运算符
具体表格略
可空逻辑
Nullable<T>之间也是可以进行逻辑运算的
具体真值表略
as运算符
object o = 5;
int? x = o as int;
如果o可以转为int,那么x就hasValue,且Value=5
否则x就是!hasValue的
??合并运算符
var x = a ?? b;
如果a不是null,那么x等于a,否则x等于b
迭代器
简介
- IEnumerable接口,代表可迭代序列
- IEnumerator接口,代表访问可迭代序列的一个游标
实现IEnumerator的类,代表了一个迭代器,需要提供一个可以完整迭代的序列以及迭代规则
public class TestEnumerator : IEnumerator<int>
{
private int index;
object IEnumerator.Current => Current;
public int Current
{
get
{
index++;
return index - 1;
}
}
public bool MoveNext()
{
return index <= 5;
}
}
其中Current是迭代序列,会一直返回index,而MoveNext则是判断是否可以继续迭代,很明显当index>5时,迭代结束
实现IEnumerable的类,需要返回一个迭代器
public class TestEnumerable : IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
return new TestEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
此时就可以使用foreach语法糖了
public static void Main(string[] args)
{
var test = new TestEnumerable();
foreach (var index in test)
{
Console.WriteLine(index);
}
}
代码等价于
public static void Main(string[] args)
{
var test = new TestEnumerable();
var iterator = test.GetEnumerator();
while (iterator.MoveNext())
{
Console.WriteLine(iterator.Current);
}
}
延迟执行
迭代器重要的作用之一就是延迟执行,考虑实现一个斐波那契数列
public static void Main(string[] args)
{
foreach (var value in Get())
{
if (value>1000)
break;
Console.WriteLine(value);
}
}
public static IEnumerable<int> Get()
{
var current = 0;
var next = 1;
while (true)
{
yield return current;
var temp = current;
current = next;
next = next + temp;
}
}