欢迎光临
我们一直在努力

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<Dog>委托返回的实例名称:" + breedDog().Name);
//将子类委托转换为父类委托 breedAnimal
Breed<Animal> breedAnimal = breedDog;
Console.WriteLine("调用Breed<Animal>委托返回的实例名称:" + 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);
}
}
}

}

赞(0)
未经允许不得转载:171主机测评 » C#学习07 协变逆变_预处理指令_迭代器
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址