C#学习07 协变逆变_预处理指令_迭代器
- 一、 协变逆变
- 1、协变逆变的概念
- 2、协变和逆变的作用
- 3、完整代码
- 二、预处理器指令
- 1、预处理器指令概念
- 2、常见的预处理器指令
- 3、完整代码
- 三、迭代器
- 1、迭代器的概念
- 2、迭代器的实现
- 3、补充
- 4、完整代码
一、 协变逆变
1、协变逆变的概念
- 协变和逆变是是用来修饰泛型占位符的
- 协变和逆变的关键词及使用
(1)协变:关键词 out
(2)逆变:关键词 in
(3)使用:协变和逆变只有在泛型接口或泛型委托中使用
2、协变和逆变的作用
- (1)用out修饰的泛型,只能作为返回值使用
- (2)用in修饰的泛型,只能作为参数使用
- (3)在里氏替换原则中参与转换
(a)关键词 out ,实现子类变父类
(b)关键词 in , 实现父类变子类
运行结果
3、完整代码
- 协变和逆变相关的完整代码
namespace Article_7_协变逆变
{
public class Animal
{
public virtual string Name { get; set; } = "动物";
}
public class Dog : Animal
{
public override string Name { get; set; } = "小黑";
public Dog(string name)
{
this.Name = name;
}
}
//2.协变和逆变的作用
// (a)协变:out修饰泛型占位符,仅作为返回值
public delegate T Breed<out T>();
// (b)逆变:in修饰泛型占位符,仅作为参数
public delegate void Shout<in T>(T t);
class Program
{
static void Main(string[] args)
{
Console.WriteLine("===== 协变(out)演示:子类变父类 =====");
//申明子类委托 breedDog
Breed<Dog> breedDog = () => { return new Dog("新生小狗"); };
Console.WriteLine("调用Breed委托返回的实例名称:" + breedDog().Name);
//将子类委托转换为父类委托 breedAnimal
Breed<Animal> breedAnimal = breedDog;
Console.WriteLine("调用Breed委托返回的实例名称:" + breedAnimal().Name);
//实际上他们使用的都是子类委托的方法
Console.WriteLine();
Console.WriteLine("===== 逆变(in)演示:父类变子类 =====");
//申明父类委托shoutAnimal
Shout<Animal> shoutAnimal = (Animal a) =>
{
Console.WriteLine($"动物 {a.Name} 叫");
};
shoutAnimal.Invoke(new Dog("小黑"));
//将父类委托转换为子类委托 shoutDog
Shout<Dog> shoutDog = shoutAnimal;
shoutDog.Invoke(new Dog("旺财"));
//实际上他们使用的都是父类委托的方法
}
}
#region 一、协变和逆变
//1.协变和逆变是用来修饰泛型占位符的,只有在泛型接口和泛型委托中使用
//(a)协变 关键词 out ,子类变父类
//(b)逆变 关键词 in , 父类变子类
//2.协变和逆变的作用
//(a)用out修饰的泛型,只能作为返回值
//(b)用in修饰的泛型,只能作为参数
#endregion
}
二、预处理器指令
1、预处理器指令概念
- 预处理指令以 " # " 开头,他不是语句块,所以结尾时不需要 " ; "
2、常见的预处理器指令
- #define、#undef
(1)#define 定义一个符号,
搭配 #if 使用,用于条件判断
该预处理指令前只能存在其他的预处理器指令,不能出现using、namespace、class等
(2)#undef取消定义一个符号
- #if、#elif、#else、endif
(1)#if 开始一个条件编译块,如果符号被定义则执行对应代码块
(2)#elif、#else 搭配 #if 使用,使用类似于条件语句
(3)#endif 结束一个条件编译块在该预处理指令中的代码块不依赖Main函数也可以直接执行
- #warning、#error
(1)#warning 生成编译器警告信息。
(2)#error 生成编译器错误信息。错误会导致程序无法运行,但警告不会
>
3、完整代码
- 预处理器指令相关的完整代码
#define d1
#define d2
#define d3
//取消定义d3
#undef d3
#if d3
Console.WriteLine("存在定义符号 d3");
#elif d2
Console.WriteLine("存在定义符号 d2");
#else
Console.WriteLine("不存在定义符号 d3与d2 ");
#endif
#warning 生成编译器警告信息
#error 生成编译器警告错误
namespace Article_7_预处理器指令
{
class Program
{
public static void Main(string[] args)
{
}
}
}
三、迭代器
1、迭代器的概念
- 迭代器是什么:
- 迭代器的作用:让自定义类的容器可以通过foreach进行遍历其中元素
2、迭代器的实现
- 标准实现:继承接口 IEnumerator、IEnumerable
- 继承接口 IEnumerator、IEnumerable,并实现其中的方法,流程如下
(1)引用命名空间 System.Collections;
(2)实现IEnumerable提供的用于返回迭代器的方法 GetEnumerator()
(3)IEnumerator本质为一个迭代器:实现其中可以通过foreach循环遍历容器的相关方法及属性
- 只实现其中的GetEnumerator()
3、补充
- (1)foreach的运行本质
(a)执行对象的 GetEnumerator()来获取迭代器IEnumertor
(b)执行得到IEnumerator对象中的MoveNext方法,当返回值为true时,表示当前元素合法,并移动索引
(c)将当前元素Current赋值给item
-(2)迭代器出现只能第一次迭代可以正常使用的情况
(a)原因:索引没有复位
(b)解决方法:每次获取迭代器时都进行索引复位
演示
4、完整代码
- 迭代器相关的完整代码
using System.Collections;
namespace Article_7_迭代器
{
#region 一、基础概念
//(1)什么是迭代器
//
//(2)迭代器的作用
//让自定义类可以通过foreach进行迭代读取元素
#endregion
#region 二、标准实现
//(1)实现方式:继承关键接口:IEnumerator、IEnumerable
// 命名空间:
//(a)接口IEnumerator本质为一个迭代器:实现可以通过foreach循环遍历容器的相关方法
//(b)接口IEnumerable 提供用于返回迭代器的方法GetEnumerator()
//foreach的运行本质
//(a)执行对象的 GetEnumerator()来获取迭代器IEnumertor
//(b)执行得到IEnumerator对象中的MoveNext方法,当返回值为true时,表示当前元素合法
//(c)将当前元素Current赋值给item
class CustomArray<T>:IEnumerable,IEnumerator
{
private T[] array;
public CustomArray(T[] array)
{
this.array = array;
}
#region(a)法一
//当前位置索引(自定义)
private int index =-1;
//IEnumerable提供的方法,用来实现获取IEnumertor迭代器
public IEnumerator GetEnumerator()
{
//这里进行复原索引
Reset();
return this;
}
//IEnumertor提供的属性,用来获取当前元素
public object Current => array[index];
//IEnumertor提供的方法,用来实现索引的移动和检查位置是否合法
public bool MoveNext()
{
index++;
return index < array.Length;
}
//IEnumertor提供的方法,用来复原索引
public void Reset()
{
index = -1;
}
#endregion
#region(b) 法二,只是实现GetEnumerator方法,利用yied
//public IEnumerator GetEnumerator()
//{
// for(int i = 0; i < array.Length;i++)
// {
// yield return array[i];
// }
//}
#endregion
}
#endregion
class Program
{
public static void Main(string[] args)
{
CustomArray<int> intArray = new CustomArray<int>(new int[] { 1, 2, 3,5,6,7 });
Console.WriteLine("----* 第一次遍历intArray *----");
foreach (int i in intArray)
{
Console.WriteLine(i);
}
Console.WriteLine("----* 第二次遍历intArray *----");
foreach (int i in intArray)
{
Console.WriteLine(i);
}
//Console.WriteLine("如运行结果显示,此时索引没有复位,第二次遍历失败");
//Console.WriteLine("手动复位,进行第三次遍历");
//intArray.Reset();
Console.WriteLine("----* 第三次遍历intArray *----");
foreach (int i in intArray)
{
Console.WriteLine(i);
}
}
}
}











>













