C#进阶学习

目录

  • 简单数据结构类
    • ArrayList
      • 声明
      • 增删查改
      • 遍历
      • 装箱拆箱
    • Stack
      • 声明
      • 增取查改
      • 遍历
      • 装箱拆箱
    • Queue
      • 声明
      • 增取查改
      • 遍历
    • Hashtable
      • 声明
      • 增删查改
      • 遍历
      • 装箱拆箱
  • 泛型
    • 泛型分类
    • 泛型的作用
    • 泛型约束
  • 常用泛型数据结构类
    • List
      • 声明
      • 增删查改
      • 遍历
    • Dictionary
      • 声明
      • 增删查改
      • 遍历
    • LinkedList
      • 声明
      • 增删查改
      • 遍历
    • 泛型栈和队列
  • 委托
    • 基本语法
    • 定义自定义委托
    • 声明委托
    • 使用定义好的委托
    • 委托变量可以存储多个函数(多播委托)
    • 系统定义好的委托
  • 事件
    • 事件的使用
    • 事件的作用
  • 匿名函数
    • 基本语法
    • 使用
    • 缺点
  • lambad
    • 语法
    • 使用
    • 闭包
  • List排序
  • 协变逆变
    • 协变(out)
    • 逆变(in)
    • 作用
  • 多线程
  • 预处理器指令
  • 反射
  • 特性
  • 迭代器
  • 特殊语法

请学完 C#核心学习再来学习进阶。

简单数据结构类

ArrayList

ArrayList是一个C#为我们封装好的类,它的本质是一个object类型的数组。

声明

using System.Collections;//需要引用命名空间
ArrayList array = new ArrayList();

增删查改

array.Add(1);
array.Insert(1,"123");array.Remove(1);//移除指定元素,从头往后找
array.RemoveAt(2);//移除指定位置的元素
array.Clear();//清空array[0];//得到指定位置的元素
array.Contains("123");//查看元素是否存在
array.IndexOf(1);//正向查找元素位置,找到返回位置,找不到返回-1
array.LastIndexOf(1);//反向查找元素位置(返回从头开始的索引数)array[0] = "999";//改

遍历

//长度
array.Count
//容量(避免产生过多的垃圾)
array.Capacity
//普通遍历
for(int i = 0;i < array.Count;i++)
{Console.WriteLine(array[i]);
}
//迭代器遍历
foreach(object item in array)
{Console.WriteLine(item);
}

装箱拆箱

ArrayList本质上是一个可以自动扩容的object数组,由于用万物之父来存储数据,自然存在装箱拆箱。当往其中进行值类型存储时就是在装箱,当将值类型对象取出来转换使用时,就存在拆箱。所以ArrayList尽量少用,之后会有更好的数据容器。

int i = 1;
array[0] = i;//装箱
i = (int)array[0];//拆箱

Stack

Stack是一个C#为我们封装好的类,它的本质也是object[]的数组,只是封装了特殊的存储规则。stack时栈存储容器,栈是一种先进后出数据结构,先存入的数据后获取,后存入的数据先获取。

声明

using System.Collections;
Stack stack = new Stack();

增取查改

//入栈
stack。Push(1);//出栈
object v = stack.Pop();//看栈顶
stack.Peek();
//查看元素是否存在于栈中
stack.Contains("123");//清空
stack.Clear();

遍历

//长度
stack.Count;
//用foreach遍历(从栈顶到栈底)
foreach(object item in stack)
{Console.WriteLine(item);
}
//另类遍历方式:将栈转换为object数组(从栈顶到栈底)
Object[] array = stack.ToArray();
for(int i = 0;i < array.Length;i++)
{Console.WriteLine(array[i]);
}
//循环弹栈
while(stack.Count > 0)
{object o = stack.Pop();Console.WriteLine(o);
}

装箱拆箱

同ArrayList。

Queue

Queue是一个c#为我们封装好的类,它的本质也是object[]的数组,只是封装了特殊的存储规则。Queue是队列存储容器,队列是一种先进先出的数据结构,先存入的数据先获取,后存入的数据后获取。

声明

System.Collections
Queue queue = new Queue();

增取查改

queue.Enqueue(1);//增object v = queue.Dequeue();//取queue。Peek()//查
queue.Contains(1)//查看元素是否存在于队列中queue.Clear();//改

遍历

queue.Count//长度
//foreach遍历
foreach(object item in queue)
{Console.WriteLine(item);
}
//将队列转换为数组遍历
object[] array = queue.ToArray();
for(int i = 0;i < array.Length;i++)
{Console.WriteLine(array[i]);
}
//循环出列
while(queue.Count > 0)
{object o = queue.Dequeue();Console.WriteLine(o);
}

Hashtable

Hashtable(又称散列表)是基于键的哈希代码组织起来的键/值对,它的主要作用是提高数据查询的效率,使用键来访问集合中的元素。

声明

System.Collections
Hashtable hashtable = new Hashtable();

增删查改

hashtable.Add(1,"123");//增hashtable.Remove()//只能通过键去删除
hashtable.Clear()//直接清空hashtable[1]//查
//查看是否存在
hashtable.Contains(1)//根据键检测
hashtable.ContainsKey(2)
hashtable.ContainsValue(12)//根据值检测hashtable[1] = "100.5f";//改

遍历

hashtable.Count//对数
//遍历所有键
foreach(object item in hashtable.Keys)
{Console.WriteLine(item);Console.WriteLine(hashtable[item]);
}
//遍历所有值
foreach(object item in hashtable.Values)
{Console.WriteLine(item);
}
//键值对一起遍历
foreach(DictionaryEntry item in hashtable)
{Console.WriteLine(item.Key);Console.WriteLine(item.Value);
}
//迭代器遍历法
IDictionaryEnumerator item = hashtable.GetEnumerator();
bool flag = item.MoveNext();
while(flag)
{Console.WriteLine(item.Key);Console.WriteLine(item.Value);flag = item.MoveNext();
}

装箱拆箱

同ArrayList

泛型

泛型实现了类型参数化,达到代码重用的目的,通过类型参数化来实现同一份代码上操作多种类型。泛型相当于类型占位符,定义类或方法使用替代符代表变量类型,当真正使用类或者方法时再具体指定类型

泛型分类

泛型类:

class 类名<泛型占位字母>
class TestClass<T>
{public T value;
}

泛型接口:

interface 接口名<泛型占位字母>
interface TestInterface<T>
{T Value{get;set;}
}

泛型函数:

函数名<泛型占位字母>(参数列表)
//普通类中的泛型方法
class Test2
{public void TestFun<T>(T value){Console.WriteLine(value);}
}
//泛型类中的泛型方法
class Test2<T>
{public void TestFun<K>(K k){}//这不是泛型方法public void TestFun(T t){}
}
//调用
Test2<int> tt2 = new Test2<int>();
tt2.TestFun(10);
tt2.TestFun<string>("123");
tt2.TestFun<float>(1.2f);

注意: 泛型占位字母可以有多个,用逗号分开

泛型的作用

1、不同类型对象的相同逻辑处理就可以选择泛型
2、使用泛型可以一定程度避免装箱拆箱

泛型约束

让泛型的类型有一定的限制。
关键字: where
类型:

where 泛型字母:(约束的类型)

1、值类型

while 泛型字母:struct
class Test<T> where T:struct
{public T value;public void TestFun<k>(K v) where K:struct{}
}

2、引用类型

where 泛型字母:class
class Test<T> where T:class
{public T value;public void TestFun<k>(K v) where K:class{}
}

3、存在无参公共构造函数

where 泛型字母:new()
class Test<T> where T:new()
{public T value;public void TestFun<k>(K v) where K:new(){}
}

4、某个类本身或者其派生类

where 泛型字母:类名
class Test<T> where T:Test1
{public T value;public void TestFun<k>(K v) where K:Test1{}
}

5、某个接口的派生类型

where 泛型字母:接口名
class Test<T> where T:IFly
{public T value;public void TestFun<k>(K v) where K:IFly{}
}

6、另一个泛型类型本身或者派生类型

where 泛型字母:另一个泛型字母
class Test<T,U> where T:U
{public T value;public void TestFun<k,V>(K v) where K:V{}
}

泛型组合使用:

class Test<T> where T:class,new()
{
}

多个泛型有约束:

class Test<T,K> where T:class,new() where K:struct
{
}

常用泛型数据结构类

List

List是一个c#为我们封装好的类,它的本质是一个可变类型的泛型数组,List类帮助我们实现了很多方法,如泛型数组的增删查改。

声明

using System.Collections.Generic
List<int> list = new List<int>();
List<string> list2 = new List<string>();

增删查改

list.Add(1);//增List<string> lisStr = new List<string>();
listStr.Add("123");
list2.AddRange(lisStr);
list.Remove(1);//移除指定元素
list.Remove(0);//移除指定位置的元素
list.Clear();//清空list[0];//得到指定位置的元素
list.Contains(1);//查看元素是否存在
int index = list.IndexOf(2);//正向查找元素位置,找不到返回-1
index = list.LastIndexOf(2);//反向查找元素位置,找不到返回-1list[0] = 99;//改

遍历

list.Count;//长度
list.Capacity;//容量,避免产生垃圾
//foreach遍历
foreach(int item in list)
{Console.WriteLine(item);
}

Dictionary

可以将Dictionary理解为拥有泛型的hashtable,它也是基于键的哈希代码组织起来的键值对。键值对类型从Hashtable的object变为了可以自己制定的类型。

声明

using System.Collections.Generic
Dictionary<int,string> dictionary = new Dictionary<int,string>();

增删查改

dictionary.Add(1,"123");//增dictionary.Remove(1);//只能通过键去删除,删除不存在的没反应
dictionary.Clear();//清空dictionary[2];//通过键查看值,找不到直接报错
dictionary.ContainsKey(1);//根据键查找
dictionary.ContainsValue("123");//根据值查找dictionary[1] = "555";//改

遍历

//遍历所有键
foreach(int item in dictionary.Keys)
{Console.WriteLine(item);Console.WriteLine(dictionary[item]);
}
//遍历所有值
foreach(string item in dictionary.Values)
{Console.WriteLine(item);
}
//键值对一起遍历
foreach(KeyValuePair<int,string> item in dictionary)
{Console.WriteLine(item.Key);Console.WriteLine(item.Value);
}

LinkedList

LinkedList是一个C#为我们封装好的类,它的本质是一个可变类型的泛型双向链表。

声明

using System.Collections.Generic
LinkedList<int> linkList = new LinkedList<int>();

增删查改

linkedList.AddLast(10);//在链表尾部添加元素
linkedList.AddFirst(20);//在链表头部添加元素
//在某一个节点之后添加一个结点
LinkedListNode<int> n = linkedList.Find(20);
linkedList.AddAfter(n,15);
//在某一个节点之前添加一个节点
LinkedListNode<int> n = linkedList.Find(20);
linkedList.AddBefore(n,15);linkedList.RemoveFirst();//移除头节点
linkedList.RemoveLast();//移除尾结点
linkedList.Remove(20);//移除指定结点
linkedList.Clear();//清空LinkedListNode<int> first = linkedList.First;//头节点
LinkedListNode<int> last = linkedList.Last;//尾结点
LinkedListNode<int> node = linkedList.Find(3);//找到指定值的结点
linkedList.Countains(1);//判断是否存在linkedList.First.Value = 10;//改

遍历

//foreach遍历
for(int item in LinkedList)
{Console.WriteLine(item);
}
//通过节点遍历
LinkedListNode<int> nowHead = linkedList.First;
while(nowHead != null)
{Console.WriteLine(nowNode.Value);nowNode = nowNode.Next;
}

泛型栈和队列

using System.Collections.Generic
Stack<int> stack = new Stack<int>();
Queue<int> queue = new Queue<int>();

使用上和stack和queue一毛一样。

委托

委托是函数的容器,可以理解为表示函数的变量类型,用来存储、传递函数。委托本质上是一个类,用来定义函数的类型(返回值和参数的类型),不同的函数必须对应和各自“格式”一致的委托。

基本语法

关键字: delegate
可以声明在namespace和class语句块中。

访问修饰符 delegate 返回值 委托名(参数列表);

定义自定义委托

1、访问修饰默认不写为public,在别的命名空间中也能使用。
2、委托的规则的声明是不能重名的(在同一语句块中)

delegate void MyFun();
public delegate int MyFun2(int a);

声明委托

static void Fun()
{}
MyFun f = new MyFun(Fun);
f.Invoke();//调用
MyFun f2 = Fun;
f2();//调用

使用定义好的委托

1、作为类的成员
2、作为函数的参数

class Test
{public MyFun fun;public MyFun2 fun2;public void TestFun(MyFun fun,MyFun2 fun2){}public void AddFun(MyFun fun,MyFun2 fun2){this.fun += fun;this.fun2 += fun2;}public void RemoveFun(MyFun,MyFun2 fun2){this.fun -= fun;this.fun2 -= fun2;}
}  

委托变量可以存储多个函数(多播委托)

MyFun ff = Fun;
ff += Fun;//ff存储了两个Fun,说明Fun被执行了两次
ff();

系统定义好的委托

//无参无返回值
using System;
Action action = Fun;
action += Fun3;
action();//可以指定返回值类型的泛型委托
static string Fun4()
{return "";
}
Func<string> funString = Fun4;//可以传n个参数的 系统提供了1到16个参数的委托
Action<int,string,bool> action2 = Fun6;//可以传n个参数的并且有返回值的,系统提供了16个委托
Func<int,int> func2 = Fun2;

事件

事件是基于委托的存在,事件是委托的安全包裹,让委托的使用更具有安全性。事件是一种特殊的变量类型。

事件的使用

访问修饰符 event 委托类型 事件名;
class Test
{public Action myFun;public event Action myEvent;public Test(){myFun = TestFun;myFun += TestFun;myFun -= TestFun;myFun();myFun.Invoke();myFun = null;myEvent = TestFun;myEvent += TestFun;myEvent -= TestFun;myEvent();myEvent.Invoke();myEvent = null;

1、事件是作为成员变量存在于类中
2、委托怎么用事件就怎么用
事件与委托的区别:
1、不能在类外部赋值
2、不能在类外部调用
注意: 事件只能作为成员存在于类和接口以及结构体中。

事件的作用

1、防止外部随意置空委托。
2、防止外部随意调用委托。
4、事件相当于对委托进行了一次封装,让其更加安全。

匿名函数

匿名函数的使用主要是配合委托和事件进行使用,脱离委托和事件是不会使用匿名函数的。

基本语法

何时使用:
1、函数中传递委托参数时
2、委托或事件赋值时

delegate (参数列表)
{};

使用

//无参无返回
Action a = delegate()
{Console.WriteLine("匿名函数逻辑");
}
a();
//有参
Action<int,string> b = delegate(int a,string b)
{
}
b(100,"123");
//有返回值
Fun<string> c = delegate()
{return "123123";
}
c();

可以作为参数传递和返回值使用

缺点

添加到委托或事件容器中后不记录无法单独移除。

lambad

可以将lambad表达式理解为匿名函数的缩写,它除了写法不同外,使用上和匿名函数一模一样,都是和委托或事件配合使用的。

语法

(参数列表)=>
{
}

使用

//无参无返回值
Action a = ()=>
{Console.WriteLine("123");
}
//有参
Action<int> a2 = (int value)=>
{Console.WriteLine("123");
}
//参数类型可以省略,参数类型和委托或事件容器一致
Action<int> a3 = (value)=>
{ 
}
//有返回值
Fun<string,int> a4 = (value)=>
{return 1;
}

其他传参和缺点与匿名函数一样

闭包

内层的函数可以引用包含在它外层的函数的变量,即使外层函数的执行已经终止。该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。

class Test
{public event Action action;public Test(){int value = 10;action = () =>{Console.WriteLine(value);};}
}

List排序

List自带排序方法

list.Sort();

自定义类排序

//继承接口IComparable
class Item : IComparable<Item>
{public int money;public Item(int money){this.money = money;}public int CompareTo(Item other){//返回值://小于0:放在传入对象的前面//等于0:保持当前的位置不变//大于0:放在传入对象的后面if(this.money > other.money) return 1;else return -1;return 0;}
}

利用委托函数排序

static int SortShopItem(ShopItem a,ShopItem b)
{if(a.id > b.id)return 1;else return -1;
}
shopItem.Sort(SortShopItem);

协变逆变

用于在泛型中修饰泛型字母的,只有泛型接口和泛型委托能使用。

协变(out)

和谐的变化,自然的变化,因为里氏替换原则父类可以装子类,所以子类变父类感受是和谐的,例如:string变成object。

逆变(in)

逆常规的变化,不正常的变化,因为里氏替换原则父类可以装子类,但是子类不能装父类。所以父类变子类感受是不和谐的,例如object变成string。

作用

//用out修饰的泛型只能作为返回值
delegate T TestOut<out T>(T v);
//用in修饰的泛型只能作为参数
delegate void TestIn<int T>(T t);
//遵循里氏替换原则
//协变:父类泛型委托装子类泛型委托
//逆变:子类泛型委托装父类泛型委托

多线程

进程:
进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。即打开一个应用程序就是在操作系统上开启了一个进程,进程之间可以相互独立运行、互不干扰,进程之间也可以相互访问、操作。
线程:
操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位,一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,我们目前写的程序中,都在主线程中。简单理解线程就是代码从上到下运行的一条“管道”。
多线程:
我们可以通过代码开启新的线程,可以同时运行代码的多条“管道”就叫多线程。

//引用命名空间
using System.Threading;
//1、申明一个新的线程
//注:线程执行的代码需要封装到一个函数中
static void NewThreadLogic()
{
}
Thread t = new Thread(NewThreadLogic);
//2、启动线程
t.Start();
//3、设置为后台线程
//当前台线程都结束了的时候,整个程序也就结束了,即使还有后台线程正在运行
//后台线程不会防止应用程序的进程被终止掉
//如果不设置为后台线程,可能导致进程无法正常关闭
t.IsBackground = true;
//4、关闭释放一个线程
//如果开启的线程中不是死循环,是能够结束的逻辑,那么不用去刻意的关闭它。
//如果是死循环,想要中止这个线程,有两种方式
//死循环中加bool
bool isRunning = false;
while(isRunning)
{
}
//通过线程提供的方法(注意在.Net core版本中无法中止,会报错)
t.Abort();
t = null;
//为了防止报错,可以加个异常处理(try……catch)
//5、线程休眠
//让线程休眠多少毫秒,在哪个线程里执行,就休眠哪个线程
Thread.Sleep(1000);

线程之间共享数据:
多个线程使用的内存是共享的,都属于该应用程序(进程)。所以要注意当多线程同时操作同一片内存区域时可能会出问题,可以通过加锁的形式避免问题。

//lock
//当我们在多个进程当中想要访问同样的东西,进行逻辑处理时,为了避免不必要的逻辑顺序执行的差错
//lock(引用类型对象)
static object obj = new object();
while(true)
{lock(obj){//代码逻辑1}
}
while(true)
{lock(obj){//代码逻辑2}
}

多线程的意义:
可以用多线程专门处理一些复杂耗时的逻辑,比如寻路、网络通信等等。

预处理器指令

编译器:
编译器是一种翻译程序,它用于将源语言程序翻译为目标语言程序。

源语言程序:某种程序设计语言写成的,如C#、C、C++、Java等语言写的程序。
目标语言程序:二进制数表示的伪机器代码写的程序。

预处理器指令:
1、预处理器指令指导编译器在实际编译开始之前对信息进行预处理。
2、预处理器指令都是以#开始。
3、预处理器指令不是语句,所以它们不以分号;结束。
4、目前我们经常用到的折叠代码块就是预处理器指令。
常见的预处理器指令:
1、#define:定义一个符号,类似一个没有值的变量。
#undef:取消define定义的符号,让其失效。
两者都是写在脚本文件最前面,一般配合if指令使用或配合特性
2、#if、#elif、#else、#endif
和if语句规则一样,一般配合#define定义的符号使用,用于告诉编译器进行编译代码的流程控制。

#define Unity4
#define Unity2017
//如果发现有Unity4这个符号,那么其中包含的代码就会被编译器编译
#if Unity4Console.WriteLine("123");
#elif Unity2017Console.WriteLine("2017");
#else Console.WriteLine("0");
#endif

3、#warning、#error
告诉编译器是报警还是报错误,一般还是配合if使用。

#warning 这个版本不合法
#error 这个版本不准运行

反射

程序集:
程序集是经由编译器得到的,供进一步编译执行的那个中间产物,在Windows系统中,它一般表现为后缀为.dll(库文件)或者是.exe(可执行文件)的格式。程序集就是我们写的一个代码集合,我们现在写的所有代码最终都会被编译器翻译为一个程序集供别人使用,如一个代码库文件(dll)或者一个可执行文件(exe)。
元数据:
元数据就是用来描述数据的数据,这个概念不仅仅用于程序上,在别的领域也有元数据。程序中的类、类中的函数,变量等信息就是程序的元数据。有关程序以及类型的数据被称为元数据,他们保存在程序集中。
反射:
程序正在运行时,可以查看其他程序集或者自身的元数据。一个运行的程序查看本身或者其它程序的元数据的行为就叫做反射。在程序运行时,通过反射可以得到其他程序集或者自己程序集代码的各种信息:类、函数、变量、对象等等,实例化它们,执行它们,操作他们。
反射的作用:
因为反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性。
1、程序运行时得到所有元数据,包括元数据的特性
2、程序运行时,实例化对象,操作对象。
3、程序运行时创建新对象,用这些对象执行任务。

class Test
{private int i = 1;public int j = 0;public string str = "123";public Test(){}public Test(int i){this.i = i;}public Test(int i,string str):this(i){this.str = str;}public void Speak(){Console.WriteLine(i);}
}

Type(类的信息类):
它是反射功能的基础,是访问元数据的主要方式。使用Type的成员获取有关类型声明的信息,有关类型的成员(如构造函数、方法、字段、属性和类的事件)。

//1、万物之父object中的GetType()可以获取对象的Type
int a = 42;
Type type = a.GetType();
Console.WriteLine(type);
//2、通过typeof关键字传入类名也可以得到对象的Type
Type type2 = typeof(int);
Console.WriteLine(type2);
//3、通过类的名字也可以获取类型
//注意类名必须包含命名空间,不然找不到
Type type3 = Type.GetType("System.Int32");
Console.WriteLine(type3);

得到类的程序集信息:

//可以通过Type得到类型所在程序集信息
Console.WriteLine(type.Assembly);

获取类中的所有公共成员:

//首先得到Type
Type t = typeof(Test);
//引用命名空间
using System.Reflection;
//然后得到所有公共成员
MemberInfo[] infos = t.GetMembers();
for(int i = 0;i < infos.Length;i++)
{Console.WriteLine(infos[i]);
}

获取类的公共构造函数并调用:

//1、获取所有构造函数
ConstructorInfo[] ctors = t.GetConstructors();
for(int i = 0;i < ctors.Length;i++)
{Console.WriteLine(ctors[i]);
}
//2、获取其中一个构造函数并执行
//得到构造函数传入Type数组,数组中内容按顺序是参数类型。
//执行构造函数传入object数组表示按顺序传入的参数
//Ⅰ得到无参构造
ConstructorInfo info = t.GetConstructor(new Type[0]);
Test obj = info.Invoke(null) as Test;//因为无参所以传null
//Ⅱ得到有参构造
ConstructorInfo info2 = t.GetConstructor(new Type[] {typeof(int)});
obj = info2.Invoke(new object[]{2}) as Test;ConstructorInfo info3 = t.GetConstructor(new Type[] {typeof(int),typeof(string)});
obj = info3.Invoke(new object[]{4,"123"}) as Test;

获取类的公共成员变量:

//1、得到所有成员变量
FieldInfo[] fieldInfos = t.GetFields();
for(int i = 0;i < fieldInfos.Length;i++)
{Console.WriteLine(fieldInfos[i]);
}
//2、得到指定名称的公共成员变量
FieldInfo infoJ = t.GetField("j");
Console.WriteLine(infoJ);
//3、通过反射获取和设置对象的值
Test test = new Test();
test.j = 99;
test.str = "2222";
//Ⅰ通过反射获取对象的某个变量的值
Console.WriteLine(infoJ.GetValue(test));
//Ⅱ通过反射设置指定对象的某个变量的值
infoJ.SetValue(test,100);
Console.WriteLine(infoJ.GetValue(test));

获取类的公共成员方法:

//通过Type类中的GetMethod方法得到类中的方法
//MethodInfo是方法的反射信息
Type strType = typeof(string);
//1、如果存在方法重载用Type数组表示参数类型
MethodInfo[] methods = strType.GetMethods();
for(int i = 0;i < methods.Length;i++)
{Console.WriteLine(methods[i]);
}
MethodInfo subStr = strType.GetMethod("Substring",new Type[] {typeof(int),typeof(int)});//Substring方法,后面是两个参数开始位置,长度
//2、调用该方法
//注意:如果是静态方法,Invoke中的第一个参数传null即可。
string str = "Hello,World!";
object result = substr.Invoke(str,new object[] {7,5});
Console.WriteLine(result);

其他:
①得枚举:GetEnumName、GetEnumNames
②得事件:GetEvent、GetEvents
③得接口:GetInterface、GetInterfaces
④得属性:GetProperty、GetPropertys
等等。
Activator:
用于快速实例化对象的类,用于将Type对象快捷实例化为对象,先得到Type,然后快速实例化一个对象。

Type testType = typeof(Test);
//1、无参构造
Test testObj = Activator.CreateInstance(testType) as Test;
//2、有参构造
testObj = Activator.CreateInstance(testType,99) as Test;

Assembly(程序集类):
主要用来加载其他程序集,加载后,才能用Type来使用其他程序集中的信息,如果想要使用不是自己程序集中的内容,需要先加载程序集,比如dll文件(库文件),简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类。

//三种加载程序集的函数
//一般用来加载同一文件下的其他程序集
Assembly assembly2 = Assembly.Load("程序集名称");
//一般用来加载不在同一文件下的其他程序集
Assembly assembly = Assembly.LoadFrom("包含程序集清单的文件的名称或路径");
Assembly assembly3 = Assembly.LoadFile("要加载的文件的完全限定路径");

类库工程的创建:
在这里插入图片描述

特性

特性是一种允许我们向程序集添加元数据的语言结构,它是用于保存程序结构信息的某种特殊类型的类。特性提供功能强大的方法以将声明信息与C#代码(类型、方法、属性等)相关联。特性与程序实体关联后,即可在运行时使用反射查询特性信息。特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集中,它可以放置在几乎所有的声明中(类、变量、函数等等申明)。
特性本质是个类,我们可以利用特性类为元数据添加额外信息。比如一个类,成员变量、成员方法等等为他们添加更多的额外信息,之后可以通过反射来获取这些额外信息。
自定义特性:

//继承特性基类:Attribute
class MyCustomAttribute : Attribute
{public string info;public MyCustomAttribute(string info){this.info = info;}
}

特性的使用:

//基本语法
[特性名(参数列表)]
//本质上就是在调用特性类的构造函数,写在类、函数、变量上一行,表示他们具有该特性信息。
[MyCustom("这个是我自己写的一个用于计算的类")]
class MyClass
{[MyCustom("这是一个成员变量")]public int value;[MyCusto("这是一个用于计算加法的函数")]public void TestFun([MyCustom("函数参数")] int a){}
}
MyClass mc = new MyClass();
Type t = mc.GetType();
//判断是否使用了某个特性
//参数一:特性的类型
//参数二:代表是否搜索继承链(属性和事件忽略此参数)
if(t.IsDefined(typeof(MyCustomAttribute),false))
{Console.WriteLine("该类型应用了MyCustom特性");
}
//获取Type元数据中的所有特性
object[] array = t.GetCustomAttributes(true);
for(int i = 0;i < array.Length;i++)
{if(array[i] is MyCustomAttribute){Console.WriteLine(array[i] as MyCustomAttribute).info);(array[i] as MyCustomAttribute).TestFun();}
}

限制自定义特性的使用范围:
通过为特性类加特性限制其使用范围

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct,AllowMultiple = true,Inherited = true)]
//参数一:AttributeTargets--特性能够用在哪些地方
//参数二:AllowMultiple--是否允许多个特性实例用在同一个目标上
//参数三:Inherited--特性是否能被派生类和重写成员继承

系统自带特性–过时特性(Obsolete)
用于提示用户使用的方法等成员已经过时,建议使用新方法,一般加在函数前的特性。

class TestClass
{//参数一:调用过时方法时提示的内容//参数二:true:使用该方法时会报错//false:使用该方法时直接警告[Obsolete("OldSpeak方法已经过时了,请使用Speak方法"true)]public void OldSpeak(string str){}public void Speak(){}
}

系统自带特性–调用者信息特性:
需要引用命名空间using System.Runtime.CompilerServices
1、哪个文件调用?
CallerFilePath
2、哪一行调用?
CallerLineNumber
3、哪个函数调用?
CallerMemberName
一般作为函数的参数的特性

public void SpeakCaller([CallerFilePath]string str,[CallerLineNumber]string fileName = "",int line = 0,[CallerMemberName]string target = "")
{
}

系统自带特性–条件编译特性(Conditional):
需要引用命名空间using System.Diagnostics;
它和预处理指令#define配合使用,主要可以用在一些调试代码上,有时想执行,有时不想执行的代码。

#difine Fun
[Conditional("Fun")]
static void Fuc()
{Console.WriteLine("Fun执行");
}

系统自带特性–外部Dll包函数特性(DllImport):
需要引用命名空间using System.Runtime.InteropServices;
用来标记非.Net(C#)的函数,表明该函数在一个外部的DLL中定义。一般用来调用 C 或者 C++的Dll包写好的方法。

[DllImport("Test.dll")]
public static extern int Add(int a,int b);

迭代器

迭代器有时又称为光标(cursor),是程序设计的软件设计模式,迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素,而又不是暴露其内部的标识。在表现效果上看,是可以在容器对象(例如链表或数组)上遍历访问的接口,设计人员无需关心容器对象的内存分配的实现细节,可以用foreach遍历的类、都是实现了迭代器的。
标准迭代器的实现方法:
1、关键接口:IEnumerator、IEnumerable
2、命名接口:using System.Collections
可以通过同时继承IEnumerable和IEnumerator实现其中的方法。

class CustomList : IEnumerable,IEnumerator
{private int[] list;//从-1开始的光标,用于表示数据得到了哪个位置private int position = -1;public CustomList(){list = new int[] {1,2,3,4,5,6,7,8};}public IEnumerator GetEnumerator(){Reset();return this;}public object Current{get{return list[position];}}public bool MoveNext(){++position;return position < list.Length;}public void Reset(){position = -1;}
}
CustomList list = new CustomList();
//1、先获取in后面这个对象的IEnumerator,会调用对象其中的GetEnumerator方法来获取
//2、执行得到这个IEnumerator对象中的MoveNext方法
//3、只要MoveNext方法的返回值是true,就会得到当前的值,然后复制给item
foreach(int item in list)
{}

用yield return语法糖实现迭代器:
yidld return是C#提供给我们的语法糖,所谓语法糖,也称糖衣语法。主要作用是将复杂逻辑简单化,可以增加程序的可读性,从而减少程序代码出错的机会。

1、关键接口:IEnumerable
2、命名空间:using System.Collections
让想要通过foreach遍历的自定义类实现接口中的方法GetEnumerator即可

class CustomList2 : IEnumerable
{private int[] list;public CustomList2(){list = new int[] {1,2,3,4,5,6,7,8};}public IEnumerator GetEnumerator(){for(int i = 0;i < list.Length;i++){//yield关键字配合迭代器使用//可以理解为暂时返回保留当前的状态,一会还会回来yield return list[i];}}
}
//遍历
CustomList2 list2 =  new CustomList2();
foreach(int item in list2)
{Console.WriteLine(item);
}

特殊语法

var隐式类型:
var是一种特殊的变量类型,它可以用来表示任意类型的变量。
注意:
1、var不能作为类的成员,只能用于临时变量声明时,一般写在函数语句块中。
2、var必须初始化

var i = 5;
var array = new int[] {1,2,3,4};
var list = new List<int>();

设置对象初始值:
声明对象时,可以通过直接写大括号的形式初始化公共成员变量和属性

class Person 
{private int money;public bool sex;public string Name;{	get;set;}public int Age{get;set;}
}
Person p = new Person{sex = true,Age = 18,Name = "123"};
Person p2 = new Person{Age = 18};

设置集合初始值:
声明集合对象时,也可以通过大括号直接初始化内部属性。

int[] array2 = new int[]{1,2,3,4,5};
List<int> ListInt = new List<int>(){1,2,3,4,5};

匿名类型:
var变量可以声明为自定义的匿名类型

var v = new {age = 10,money = 11,name = "小明"};
Console.WriteLine(v.age);

可空类型:

//1、值类型是不能赋值为空的
int c = null;
//2、声明时在值类型后面加?可以赋值为空
int? c = null;
//3、判断是否为空
if(c.HashValue)
{
}
//4、安全获取可空类型值
int? value = null;
//Ⅰ如果为空,默认返回值类型的默认值
Console.WriteLine(value.GetValueDefault());
//Ⅱ也可以指定一个默认值
Console.WriteLine(value.GetValueDefault(100));

空合并操作符:
空合并操作符:??

左边值 ?? 右边值
//如果左边值为null,就返回右边值,否则返回左边值
//只要是可以为null的类型都能用
int? intV = null;
int intI = intV ?? 100;

内插字符串:
关键符号:$
$来构造字符串,让字符串中可以拼接变量

string name = "123";
Console.WriteLine($"好好学习,{name}");

单句逻辑简略写法:

if(true)Console.WrtieLine("234");
for(int i = 0;i < n;i++)Console.WriteLine(i);
while(true)Console.WriteLine("123");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/620116.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式⑤ :一致性

一、前言 有时候不想动脑子&#xff0c;就懒得看源码又不像浪费时间所以会看看书&#xff0c;但是又记不住&#xff0c;所以决定开始写"抄书"系列。本系列大部分内容都是来源于《 图解设计模式》&#xff08;【日】结城浩 著&#xff09;。该系列文章可随意转载。 …

【Docker】Linux中Docker镜像结构及自定义镜像,并且上传仓库可提供使用

目录 一、镜像结构 1. 基本结构 2. 常用命令 二、自定义镜像 1. 基本镜像 2. 进阶镜像 3. 完善镜像 三、镜像上传仓库 每篇一获 一、镜像结构 自定义 Docker 镜像有很多用途&#xff0c;以下是一些主要的应用场景&#xff1a; 一致性环境&#xff1a;通过自定义镜像&a…

Gauss消去法(C++)

文章目录 算法描述顺序Gauss消去法列选主元Gauss消去法全选主元Gauss消去法Gauss-Jordan消去法 算法实现顺序Gauss消去法列选主元Gauss消去法全选主元Gauss消去法列选主元Gauss-Jordan消去法 实例分析 Gauss消去法是求解线性方程组较为有效的方法, 它主要包括两个操作, 即消元和…

正则表达式Regex

是什么&#xff1a;一句话&#xff0c;正则表达式是对字符串执行模式匹配的技术。 从一段字符串中提取出所有英文单词、数字、字母和数字。 如果采用传统方法&#xff1a;将字符串的所有字符分割成单个&#xff0c;根据ASCII码判断&#xff0c;在一定范围内就是字母&#xff…

pymssql 报错误解决办法:20002, severity 9

错误 解决办法 python3.6&#xff0c;安装pymssql低版本&#xff08;pymssql-2.1.5-cp36-cp36m-win32.whl&#xff09;

【教3妹学编程-算法题】构造限制重复的字符串

3妹&#xff1a;“太阳当空照&#xff0c;花儿对我笑&#xff0c;小鸟说早早早&#xff0c;你为什么背上炸药包” 2哥 :3妹&#xff0c;什么事呀这么开森。 3妹&#xff1a;2哥你看今天的天气多好啊&#xff0c;最近一周都是大晴天&#xff0c;艳阳高照 2哥&#xff1a;是啊&am…

相比其他关系型数据库,亚信安慧AntDB JDBC驱动特性有哪些不同之处

关键字&#xff1a;JDBC&#xff0c;双引擎语法&#xff0c;安全加强&#xff0c;批量更新 使用Java语言进行各类应用程序的快速开发成为目前比较主要且流行的开发方式。JDBC是 Java 语言中用来连接和操作关系型数据库的 API&#xff0c;在业务程序与关系型数据库通信时&#…

一文了解Git(所有命令)附带图片

我是南城余&#xff01;阿里云开发者平台专家博士证书获得者&#xff01; 欢迎关注我的博客&#xff01;一同成长&#xff01; 一名从事运维开发的worker&#xff0c;记录分享学习。 专注于AI&#xff0c;运维开发&#xff0c;windows Linux 系统领域的分享&#xff01; 其他…

机器学习算法理论:线性回归

线性回归 回归的理论解释&#xff1a;回归分析是确定两种或两种以上变数间相互依赖的定量关系的一种统计分析方法。按照自变量和因变量之间的关系类型&#xff0c;可分为线性回归分析和非线性回归分析。 如果在回归分析中&#xff0c;只包括一个自变量和一个因变量&#xff0c;…

【Linux】Linux基础之权限

目录 一、Linux中的用户1.1 用户之间的身份切换1.2 指令提权 二、权限管理2.1 文件权限2.2 权限操作2.3 chown和chgrp 三、文件类型四、目录文件的权限操作五、权限掩码六、粘滞位 一、Linux中的用户 Linux中主要有两种用户&#xff1a; root&#xff0c;超级用户非root&…

Netfilter 是如何工作的(六):连接跟踪信息的入口创建(in)和出口确认(confirm)

Articles (gitee.io) IPtables-朱双印博客 (zsythink.net) 在 Netfilter 是如何工作的(五) 中连接跟踪信息使用的创建-确认机制的 Netfilter在报文进入系统的入口处&#xff0c;将连接跟踪信息记录在报文上&#xff0c;在出口进行confirm.确认后的连接信息 本文以一个本机上送…

【MATLAB】CEEMDAN+FFT+HHT组合算法

代码原理 集成经验模态分解&#xff08;CEEMDAN&#xff09;是一种信号处理方法&#xff0c;旨在将非线性和非平稳信号分解为本质模态函数&#xff08;IMF&#xff09;。这种方法通过对信号进行多轮迭代&#xff0c;结合了噪声干扰的累计退化&#xff0c;从而更好地处理了信号…

关于React你必须知道的3个错误用法。

1. 你知道如何使用“&&”吗? 在React程序中,我经常使用“&&”运算符来决定是否显示内容,如下所示: 我的组长: “你不知道&&运算符的特性吗?当请求还没有成功返回时,会直接渲染“0”。” 我并不信服, 因为我一直都是这样编写代码,从未出过错。为了…

计算机网络技术-2022期末考试解析

【前言】 这是计算机网络技术这门课&#xff0c;感觉和计网还是有不一样的&#xff0c;但也有能做的&#xff0c;把能做的做了。 一、单项选择题&#xff08;每题2分&#xff0c;共20分&#xff09; 1. 用于测试两台计算机连通状况的命令是 。 ( ) A. cmd B. ping C. ipconf…

Java副本的概念

在Java中&#xff0c;"副本"&#xff08;copy&#xff09;一词可以用于描述不同的概念&#xff0c;具体取决于上下文。以下是两个常见的用法&#xff1a; 对象的副本&#xff1a;在Java中&#xff0c;当你创建一个对象并将其赋值给另一个变量时&#xff0c;实际上是创…

配置CentOS系统以支持静态HTTP服务

CentOS是一个流行的Linux发行版&#xff0c;广泛应用于服务器环境。要配置CentOS系统以支持静态HTTP服务&#xff0c;您可以按照以下步骤进行操作&#xff1a; 安装Web服务器软件&#xff1a;CentOS自带了Apache HTTP服务器软件&#xff0c;您可以使用以下命令安装它&#xff1…

Go 知多少?

作为一名已接触过其他语言的开发&#xff0c;再去学习一门新语言可比之前轻松不少&#xff0c; 语言之间存在很多相似点&#xff0c;但是新语言也有自己的不同点&#xff0c;通常我会先了解它与其他语言常遇到的不同点有哪些&#xff0c; 使自己先能够上手编写基础程序&#…

Java多线程:初识多线程!左手画方,右手画圆

&#x1f451;专栏内容&#xff1a;Java⛪个人主页&#xff1a;子夜的星的主页&#x1f495;座右铭&#xff1a;前路未远&#xff0c;步履不停 目录 一、线程与进程二、创建线程方法1、继承Thread类2、实现Runnable接口3、两者区别4、举个栗子5、简洁写法Ⅰ、Thread匿名内部类写…

DNS 正/反向解析 主从复制 分离解析

一 DNS概念它作为将域名和IP地址相互映射的一个分布式数据库&#xff0c;能够使人更方便地访问互联网 每一台 DNS 服务器都只负责管理一个有限范围 根域: 全球根服务器节点只有13个,10个在美国&#xff0c;1个荷兰&#xff0c;1个瑞典&#xff0c;1个日本 一级域名&#xff…

【已解决】C语言实现多线程的同步与异步

说真的写了这篇博文时&#xff0c;才知道c语言本身不支持多线程&#xff0c;而是一些windowsapi让c语言拥有多线程的能力&#xff0c;那下面内容就以打开对话框为例&#xff0c;展现如何实现多线程的同步与异步。 文章目录 问题起源c语言多线程同步方案c语言多线程异步方案总结…