入门
C#程序在.NET上运行,.NET framework包含两个部分:
①:.NET framework类库
②:公共语言运行库CLR(.NET虚拟机)
CLS(公共语言规范) CTS(通用类型系统)
.NET 还包含大量库,这些库支持多种不同的工作负载。
编译工具:
使用visual studio code 新建控制台,进入终端(Ctrl+~),选择好文件存储位置之后,输入dotnet命令即可;
dotnet new console //新建控制台应用
使用visual studio 2022 新建项目,选择控制台(.NET Framwork);
using System;
class Program
{ static void Main() { Console.WriteLine("Hello, World!"); }
}
请看大屏幕,程序员入门之向世界说你好,
Main静态方法是C#程序的入口点。
所有代码都必须包含在类中,类的文件后缀名为.cs,最上面是using,然后是命名空间→类→属性和方法→代码块;
数据类型
C#中有两大类,值类型和引用类型
值类型
包含数据的实际值,而不是引用。类似于Java中的四类八种,这里就不一一例举了,可以百度下。
引用类型
存储了一个指向对象的引用,包括常见的类、字符串、数组、接口、委托、Null,dynamic等。
类型判断
使用var关键字,可以根据变量的初始化值"推断"变量的类型。
例如:
var a=0;
就变成
int a=0;
var定义的类型是静态的,在编译时候就被定义好了。
而dynamic定义的是动态类型,它在程序运行的时候才会确定。
值类型(Value Types)和引用类型(Reference Types)的区别
值类型:
1.直接存储数据的实际值,存储在栈(Stack)中,且内存分配和释放是⾃动完成的。每个值类型的变量都具有⾃⼰的内存空间,互相独⽴。
2.当⼀个值类型的变量被赋值给另⼀个变量,实际上是将该值的副本复制给了新变量。因此,修改⼀个变量不会影响到其他变量,它们互相独⽴。(纸张的复印,修改复印件不会影响本件)。
3.当将值类型的变量作为⽅法的参数进⾏传递时,实际上是将值的副本传递给了⽅法。在⽅法内部对参数的修改不会影响到原始变量。
4.值类型适⽤于较⼩的简单数据,具有快速的访问和复制特性。
引用类型:
1.存储的事对象的引用,也就是内存地址,对象本身存储在堆(Heap)中,多个引⽤类型的变量可以指向同⼀个对象,它们共享同⼀块内存空间。
2.当⼀个引⽤类型的变量被赋值给另⼀个变量,实际上是将对象的引⽤复制给了新变量。因此,两个变量引⽤同⼀个对象,修改其中⼀个变量会影响到另⼀个变量,它们共享同⼀块内存空间。(同⼀个云盘资源,使⽤不同链接分享给不同的⼈时,⼤家看到的资源是同⼀个资源,如果这个资源被修改了,⼤家看到的都变化了)。
3.当将引⽤类型的变量作为⽅法的参数进⾏传递时,实际上是将对象的引⽤传递给了⽅法。在⽅法内部对参数引⽤的对象进⾏的修改会影响到原始变量。
4.引⽤类型适⽤于复杂的对象、⼤型数据集合和需要共享数据的场景,但需要更多的内存开销和额外的访问层级。
装箱和拆箱
装箱和拆箱的过程就是将值类型和引用类型互相转换的过程,但是实际开发中尽量避免出现装箱和拆箱,为什么?因为损耗性能,可以使用泛型来避免。
//装箱
int number=20;
object obj=number;
//拆箱
int numberss=(int)obj;
运算符
这里也不一一介绍了,所有开发语言的运算符基本一致,极个别除外;
运算法优先级
优先级简易概括:有括号先括号,后乘除在加减,然后位移再关系,逻辑完后条件,最后⼀个逗号,。
字符串处理
字符串拼接
常规字符串处理就是定义两个string,然后拼接;
string firstName = "Taylor";string lastName = "Swift";Console.WriteLine(firstName + lastName);
//输出TaylorSwift
但是有没有想过,如果N个字符拼接呢,要写N个?那么性能呢?C#中为了提高程序性能,提供了StringBuilder方法进行拼接。
StringBuilder btf = new SreingBuilder();btf.Append("Taylor");btf.Append("Swift");Console.WriteLine(btf);
//输出TaylorSwift
字符串格式化
string.Format:
string firstName = "Taylor";string lastName = "Swift";string result = string.Format("first name is {0}, last name is {1}", firstName, lastName); Console.WriteLine(result);
//输出TaylorSwift
交互
输出用WriteLine,输入呢?ReadLine。
string btf = Console.ReadLine();Console.WriteLine(btf);
分支和循环
分支
if、if else if、switch分支
分支同Java、Python、Shell一样的。
循环
for、foreach、while、do...while循环
循环也是,foreach循环式当做集合使用(个人理解)和for没有什么区别
int[] arr = {1,2,3,4,5};foreach (int var in arr)
{Console.WriteLine(var);
}
枚举
枚举是值类型C#中,不能被继承
//定义枚举DayofWeek
enum DayofWeek{Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday}static void Main() { Console.WriteLine("Day of the week: " + DayofWeek.Monday); }
还有一种调用,如果报错因为层级的关系将实例化放在枚举上边即可。
enum DayofWeek{Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday}DayofWeek DayofWeekEnum = DayofWeek.Monday;
结构体
public struct Person
{//定义两个字段public string Name;public int Age;
} static void Main(string[] args) {Person person = new Person();//实例化
//赋值person.Name = "test";person.Age = 18;
//打印Console.WriteLine(person.Name);Console.WriteLine(person.Age);}
结构的特点:
结构可带有⽅法、字段、索引、属性、运算符⽅法和事件。
结构可定义构造函数,但不能定义析构函数。但是,不能为结构定义⽆参构造函数。⽆参构造函数(默认)是⾃动定义的,且不能被改变。
结构不能作为其他结构或类的基础结构。结构可实现⼀个或多个接⼝。结构成员不能指定为abstract、virtual或protected。当使⽤New操作符创建⼀个结构对象时,会调⽤适当的构造函数来创建结构。与类不同,结构可以不使⽤ New操作符即可被实例化。如果不使⽤New操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使⽤。
方法
功能:
代码重用
调用:类的实例.⽅法名([参数列表])静态⽅法:类名.⽅法名([参数列表])
方法分为有参和无参
static void Main() {代码块}
//Main方法(无参)static void Main(){
static void Ma(int a,int b) {Console.WriteLine($"{a},{b}");
} int c =10;int d=20;Ma(c,d);
}
//有参
异常处理
和Java、Python中的异常一样,try final finally
C#中 try、catch、finally、throw
try:⼀个try块标识了⼀个将被激活的特定的异常的代码块。后跟⼀个或多个catch块。
catch:程序通过异常处理程序捕获异常。catch关键字表⽰异常的捕获。
finally:finally块⽤于执⾏给定的语句,不管异常是否被抛出都会执⾏。例如,如果您打开⼀个⽂件,不管是否出现异常⽂件都要被关闭。
throw:当问题出现时,程序抛出⼀个异常。使⽤ throw 关键字来完成。
try
{一起异常的语句}
catch(异常(Exception(异常基类)) e)
{处理异常}
finally
{执行语句代码块}
*面向对象*
面向对象是一种编程范式,使程序更加灵活,容易修改,易于复⽤。
它将现实世界的概念和实体表⽰为程序中的对象。
在⾯向对象编程中,程序由⼀组相互作⽤的对象组成,每个对象都有⾃⼰的状态(属性)和⾏为(⽅法)。
⾯向对象编程的核⼼思想是将复杂的问题分解为⼀系列相对独⽴的对象,这些对象通过相互发送消息来协同⼯作。
每个对象都可以封装⾃⼰的数据和功能,并且可以与其他对象进⾏交互。
1.类(Class):类是对象的模板或蓝图,描述了对象的属性和⽅法。它定义了对象的结构和⾏为。
2.对象(Object):对象是类的实例。每个对象都有⾃⼰的状态和⾏为。
.3封装(Encapsulation):封装是⼀种将数据和⽅法组合在⼀起的机制,以限制对对象内部数据的直接访问。通过封装,对象的内部细节对外部是隐藏的,只能通过对象的公共接⼜访问。
4.继承(Inheritance):继承是⼀种机制,允许创建⼀个新类(⼦类)来继承现有类(⽗类)的属性和⽅法。⼦类可以重⽤⽗类的代码,并可以在不修改⽗类的情况下扩展其功能。
5.多态(Polymorphism):多态允许以统⼀的⽅式使⽤不同类的对象。它使得可以在⽗类的引⽤下操作⼦类的对象,通过⽅法的重写和⽅法的重载实现多态性。
类
类的定义是以关键字class开始,后跟类的名称。类的主体,包含在⼀对花括号内。 (使用.访问类的成员)。
访问修饰符
1.public:公共访问修饰符,表⽰成员可以从任何地⽅访问,没有限制。
2.private:私有访问修饰符,表⽰成员只能在同⼀类中访问,对外部代码不可⻅。3.protected:受保护访问修饰符,表⽰成员可以在同⼀类内部和派⽣类中访问,对外部代码不可⻅。
4.internal:内部访问修饰符,表⽰成员可以在同⼀程序集中访问。程序集是指⼀组相关的代码⽂件(通常是⼀个项⽬或⼀个库),可以是单个项⽬或多个项⽬组成。5.protectedinternal:受保护内部访问修饰符,表⽰成员可以在同⼀程序集内和派⽣类中访问。这是protected和internal的组合,提供了更⼤的访问范围。
字段
在C#中,字段(Fields)是类中⽤于存储数据的成员,可以是公共的(public)、私有的(private)或受保护的(protected)。字段存储对象的状态和数据,并且可以通过类的实例进⾏访问和操作。
属性
在C#中,属性(Properties)提供了对私有字段的访问,并允许控制对字段的访问⽅式。属性可以⽤于获取(get)和设置(set)字段的值,并且可以在访问时执⾏其他逻辑。
public static void ClassName
{private int age;public int Age{get{return age;}set{age = value;}}
}
字段和属性的区别
字段
字段可以直接访问,不受限制,无法对字段读写进行额外控制。
字段是类的成员变量。
字段只提供了简单的数据存储功能。
属性
属性通过访问器来访问(get和set),可以自定义读写逻辑。
属性可以封装字段。
属性的功能更加灵活。
构造函数
是一种特殊的方法。
构造函数可以有多个重载,每个重载可以接受不同类型和数量的参数,以满⾜不同的初始化需求。
使用构造函数的时候必须实例化(new)关键字。
构造函数在对象创建的过程中⾃动调⽤,⽤于执⾏初始化操作。可以在构造函数中设置字段的初始值、执⾏其他必要的操作,或者调⽤其他⽅法来完成初始化过程。
事件
是⼀种⽤于实现发布者-订阅者模式的机制,⽤于在对象之间传递消息和通知。事件允许⼀个对象(称为发布者)触发事件,并允许其他对象(称为订阅者)注册为事件的观察者,以便在事件发⽣时接收通知并执⾏相应的操作。
public event EventHandler Work;public void onWork(){ Work.Invoke(this, EventArgs.Empty); }//事件触发static void Main(string[] args) { //实例化Program p = new Program();//订阅事件p.Work += Program.Working;//触发事件p.onWork();}//事件public static void Working(object sender, EventArgs e){Console.WriteLine("正在工作");}
索引器
private int[] arr = new int[10];public int this[int index]{ get { return arr[index]; } set { arr[index] = value; }}static void Main(string[] args) { Program p = new Program();p[0] = 10;p[1] = 20;Console.WriteLine(p[0]);Console.WriteLine(p[1]);}
面向对象三大特性
封装
封装的意义在于保护或者防⽌代码(数据)被我们⽆意中破坏。
适当的封装可以让程式码更容易理解与维护,进⽽增强数据的安全性和简化程序的编写⼯作。
封装既可以封装成员变量,⼜可以封装成员⽅法(或属性)
static void Main() { Console.WriteLine("Hello, World!");Console.WriteLine("Hello, World!1");Console.WriteLine("Hello, World!2");Console.WriteLine("Hello, World!3");}
继承
继承是⾯向对象程序设计中最重要的概念之⼀。
继承→根据⼀个现有类来定义新类
好处:使得创建和维护应⽤程序变得更容易;
也有利于重⽤代码和节省开发时间。当创建⼀个新类时,如果这个类的某些成员和⼀个现有类相同。程序员不需要完全重新编写新的数据成员和成员函数,只需要创建⼀个新的类,继承这个现有类即可。这个已有的类被称为的基类,这个新的类被称为派⽣类。
class program:(继承的类)
多态
多态是同⼀个⾏为具有多个不同表现形式或形态的能⼒。
通俗地说:同⼀操作作⽤于不同的对象,产⽣不同的结果。
在C#中,每个类型都是多态的,因为包括⽤户定义类型在内的所有类型都继承⾃Object。
using System;class Program
{public interface IWorker
{void Work();
}public class Engineer : IWorker
{public void Work(){Console.WriteLine("Engineer is working on a project.");}
}public class Designer : IWorker
{public void Work(){Console.WriteLine("Designer is working on a design.");}
}public class Manager
{private IWorker worker;public Manager(IWorker worker){this.worker = worker;}public void AssignWork(){worker.Work();}
}public static void Main(string[] args){Engineer engineer = new Engineer();Designer designer = new Designer();Manager manager = new Manager(engineer);manager.AssignWork(); // 输出: Engineer is working on a project.manager = new Manager(designer);manager.AssignWork(); // 输出: Designer is working on a design.}
}
在C#中,多态性是指同一个接口可以被不同的类实现,从而允许这些类以不同的方式响应相同的消息。多态性通常通过继承和接口来实现。下面是一个简单的例子,展示了如何使用继承和接口来实现多态性。
首先,定义一个接口`IWorker`:
public interface IWorker
{
void Work();
}
然后,定义两个类`Engineer`和`Designer`,它们都实现了`IWorker`接口:
public class Engineer : IWorker
{
public void Work()
{
Console.WriteLine("Engineer is working on a project.");
}
}public class Designer : IWorker
{
public void Work()
{
Console.WriteLine("Designer is working on a design.");
}
}
接下来,定义一个`Manager`类,它包含一个`IWorker`类型的成员变量,并使用多态性来调用`Work`方法:
public class Manager
{
private IWorker worker;public Manager(IWorker worker)
{
this.worker = worker;
}public void AssignWork()
{
worker.Work();
}
}
最后,在`Main`方法中演示多态性的使用:
public class Program
{
public static void Main(string[] args)
{
Engineer engineer = new Engineer();
Designer designer = new Designer();Manager manager = new Manager(engineer);
manager.AssignWork(); // 输出: Engineer is working on a project.manager = new Manager(designer);
manager.AssignWork(); // 输出: Designer is working on a design.
}
}在这个例子中,`Manager`类通过`IWorker`接口与`Engineer`和`Designer`类进行交互,而不需要知道具体是哪种类型的对象。这就是多态性的体现,它允许`Manager`类以统一的方式处理不同类型的`IWorker`对象。
多态性分为两种:
静态多态
动态多态
静态多态
根据传递的参数、返回类型等决定执⾏的操作⽅法重载——同⼀个范围内对相同的函数名有多个定义。参数列表个数或者类型不同。
动态多态
直到系统运⾏时,才决定实现何种操作通过抽象⽅法和虚⽅法实现的。
虚拟
当一个方法使用virtual修饰的时候,代表是虚方法,可以override重写子类。
注意:相同的方法以及参数列表
密封
当对⼀个类应⽤sealed修饰符时,此修饰符会阻⽌其他类从该类继承。
new:继承类中的⼀个⽅法“隐藏”基类中同名的⽅法。它会取代原⽅法,⽽不是覆盖。
抽象类
关键字abstract创建抽象类。当⼀个派⽣类继承⾃该抽象类时,必须实现该抽象类中的抽象⽅法。抽象类的⼀些规则:
不能直接创建⼀个抽象类的实例。
不能在⼀个抽象类外部声明⼀个抽象⽅法。
抽象类不能被声明为sealed。
抽象类可以包含抽象⽅法,抽象⽅法可被派⽣类实现。
抽象类可以定义普通⽅法、虚⽅法,并可以有实现。
在C#中,抽象类是一种特殊的类,它不能被实例化,只能被继承。抽象类可以包含抽象方法和非抽象方法。抽象方法是一种没有实现的方法,必须在派生类中实现。下面是一个简单的例子,展示了如何使用抽象类。
using System;public abstract class Animal
{public abstract void MakeSound();public void Sleep(){Console.WriteLine("The animal is sleeping.");}
}public class Dog : Animal
{public override void MakeSound(){Console.WriteLine("The dog barks.");}
}public class Cat : Animal
{public override void MakeSound(){Console.WriteLine("The cat meows.");}
}public class Program
{public static void Main(string[] args){Animal myDog = new Dog();myDog.MakeSound(); // 输出: The dog barks.myDog.Sleep(); // 输出: The animal is sleeping.Animal myCat = new Cat();myCat.MakeSound(); // 输出: The cat meows.myCat.Sleep(); // 输出: The animal is sleeping.}
}
Animal
类是一个抽象类,它包含一个抽象方法MakeSound
和一个非抽象方法Sleep
。Dog
和Cat
类继承自Animal
类,并实现了MakeSound
方法。通过创建Animal
类型的引用,可以调用MakeSound
方法,而不需要知道具体是哪种类型的对象。这就是抽象类的使用,它允许我们以统一的方式处理不同类型的对象。
接口
接⼝本⾝并不实现任何功能,它只是和声明实现该接⼜的对象订⽴⼀个必须实现哪些⾏为的契约。
关于接口的定义请看多态的代码定义即可。
public interface inter
{void work();
}
数组
简单数组
在声明数组时,应先定义数组中元素的类型,其后是⼀对空⽅括号和⼀个变量名。例如,下⾯声明了⼀个包含整型元素的数组∶
int[] myArray;
数组初始化
声明了数组后,就必须为数组分配内存,以保存数组的所有元素。数组是引⽤类型,所以必须给它分配堆上的内存。
int[] myArray = new int[5];string[] stringArray = {1,2,3,4,5};//不同数组初始化
访问数组
在声明和初始化数组后,就可以使⽤索引器访问其中的元素了。数组只⽀持有整型参数的索引器,引用的用get、set。
int[] myArray = {1,2,3,4,5};int a = myArray[0]; //--1int b = myArray[3]; //--4//如果是复制过来的数组不知道元素个数,可以使用for/foreach遍历;
for(int i=0;i<myArray.Length;i++)
{Console.WriteLine(myArray[i]);
}foreach(int var in myArray)
{Console.WriteLine(var);
}
多维数据
多维数组⽤两个或多个整数来索引。
在C#中声明这个⼆维数组,需要在⽅括号中加上⼀个逗号。数组在初始化时应指定每⼀维的⼤⼩(也称为阶)。接着,就可以使⽤两个整数作为索引器来访问数组中的元素∶
int[,] one = new int[3,3];one[0,0] = 1;
//以此类推int[,] two = {{1,2,3},{4,5,6},{7,8,9}};//三维数组
int[,,] three = {{{1,2},{3,4}},{{5,6},{7,8}},{{9,0},{11,12}}};
Console.WriteLine(three[1,1,1]); //8
Console.WriteLine(three[0,1,1]); //4
锯齿数据
如果说一/二/三维数字式1*1/2*2/3*3,那么锯齿数据就是不规则的。
int[][] ints = new int[3][];ints[0] = new int[2] { 1, 2 };ints[1] = new int[3] { 3,4,5 };ints[2] = new int[5] { 6,7,8,9,0 };for(int i = 0; i < ints.Length; i++){for(int j = 0; j < ints[i].Length; j++){Console.WriteLine($"i:{i},j:{j},value:{ints[i][j]}");}}
复制数组
int[] intCopyArrayOne = {1,2,3};int[] intCopyArrayTwo = (int[])intCopyArrayOne.Clone();intCopyArrayTwo[0] = 100;Console.WriteLine(intCopyArrayOne[0]); //输出1Console.WriteLine(intCopyArrayTwo[0]); //输出100Console.WriteLine(intCopyArrayOne[1]); //输出2Console.WriteLine(intCopyArrayTwo[1]); //输出2Console.WriteLine(intCopyArrayOne[2]); //输出3Console.WriteLine(intCopyArrayTwo[2]); //输出3Console.WriteLine(intCopyArrayOne.Length); //输出3Console.WriteLine(intCopyArrayTwo.Length); //输出3Console.WriteLine(intCopyArrayOne == intCopyArrayTwo); //输出False
排序
string[] names = { "dog", "cat", "pig", "cow" };Array.Sort(names);foreach (string name in names) {Console.WriteLine(name);}
集合
List是与数组相当的集合类。.NETFramework为动态列表提供了泛型类List。这个类实现了IList、ICollection、Emumerable、List<ICollection和IEnumerable接⼝。
List<int> intList = new List<int>(){1,2,3};List<string> stringList = new List<string>(){"One","Two","Three"};//添加元素有Add
intList.Add(4);stringList.Add("Four");//Add只能单个添加,AddRange()批量添加;intList.AddRange(new int[]{4,5,6});stringList.AddRange(new string[]{"Four","Five","Sex"});//添加元素InsertstringList.Insert(3,"aaa");//访问元素 可以根据索引或者循环string num = stringList[3];for(int i=0;i<intList.Length;i++)
{Console.WriteLine(intList[i])
}//删除元素
//RemoveAt 根据索引删除
stringList.RemoveAt(5);
//Remove 删除指定元素
stringList.Remove("three");
//RemoveRange 批量删除
stringList.RemoveRange(3,3);//查找元素
int strListIndex = stringList.IndexOf("Two");
Console.WriteLine(strListIndex);Console.WriteLine(intList.Find(x => x>5));//查找大于5Console.WriteLine(intList.FindIndex(x => x>5));//查找大于5索引Console.WriteLine(intList.FindAll(x => x>5));//查询所有大于5//排序--Sort 快速排序List<int> intList2 = new List<int>(){3,5,0};intList2.Sort();
foreach(var item in intList2)
{Console.WriteLine(item);
}//②
List<Person> pList = new List<Person>(){new Person("Aa","Aa"),new Person("Zz","Zz"),};pList.Sort();foreach(var item in pList)
{Console.WriteLine(item);
}
集合初始值设定项只能在声明集合时使⽤。AddRange()⽅法则可以在初始化集合后调⽤。如果在创建集合后动态获取数据,就需要调⽤AddRange0。
泛型
在C#中,泛型是⼀种参数化类型的机制,它允许在定义类、结构、接⼜和⽅法时使⽤类型参数。使⽤泛型可以创建可以在多个数据类型上⼯作的通⽤代码,⽽不需要针对每种具体类型编写重复的代码。泛型的主要优势是提⾼了代码的可重⽤性、类型安全性和性能。通过使⽤泛型,可以编写更加灵活和通⽤的代码,同时在编译时进⾏类型检查,避免在运⾏时出现类型错误。
using System;/// <summary>
/// Box<T>是一个泛型类,它使用类型参数T来表示内容类型。
/// SetContent和GetContent方法都是泛型方法,它们接受和返回类型为T的参数。
/// 在Main方法中,创建了Box<int>和Box<string>的实例,并分别设置了整数和字符串内容。
/// 通过使用泛型,我们可以在不牺牲类型安全的情况下,创建可以处理不同数据类型的通用组件。
/// </summary>
/// <typeparam name="T"></typeparam>
public class Box<T>
{private T content;public void SetContent(T content){this.content = content;}public T GetContent(){return content;}
}
public class Program
{public static void Main(string[] args){Box<int> intBox = new Box<int>();intBox.SetContent(10);Console.WriteLine(intBox.GetContent()); // 输出: 10Box<string> stringBox = new Box<string>();stringBox.SetContent("Hello, World!");Console.WriteLine(stringBox.GetContent()); // 输出: Hello, World!}
}
委托
在C#中,委托(Delegate)是⼀种类型,它允许您将⽅法作为参数传递,并在需要时调⽤该⽅法。委托可以⽤于声明⽅法的签名,并且可以创建对符合该签名的任何⽅法的引⽤。委托可⽤于实现事件和回调机制,使得代码更加灵活和可扩展。它提供了⼀种在运⾏时动态决定要调⽤的⽅法的⽅式,这对于处理异步操作、事件处理程序和回调函数⾮常有⽤。
using System;/// <summary>
/// Action是一个委托,它定义了一个没有参数且没有返回值的方法签名。
/// SayHello方法与Action委托的签名兼容,因此我们可以创建一个Action委托的实例,
/// 并将SayHello方法赋值给它。然后,我们可以通过调用action来执行SayHello方法。
/// </summary>public delegate void Action();public class Program{public static void Main(string[] args){Action action = new Action(SayHello);action(); // 输出: Hello, World!void SayHello(){Console.WriteLine("Hello, World!");}}}/// <summary>
/// 在这个例子中,ActionWithParam是一个委托,它定义了一个接受一个整数参数且没有返回值的方法签名。
/// PrintNumber方法与ActionWithParam委托的签名兼容,
/// 因此我们可以创建一个ActionWithParam委托的实例,并将PrintNumber方法赋值给它。
/// 然后,我们可以通过调用action并传递参数来执行PrintNumber方法。
/// </summary>
/// <param name="param"></param>
public delegate void ActionWithParam(int param);public class Program
{public static void Main(string[] args){ActionWithParam action = new ActionWithParam(PrintNumber);action(42); // 输出: 42void PrintNumber(int number)
{Console.WriteLine(number);
}}
}
lambda/Linq
Lambda表达式是一种匿名函数,可以用于表示简单的函数逻辑。LINQ(Language Integrated Query)是一种查询语言,
1.基本语法:Lambda表达式由参数列表、箭头符号和表达式组成。
2.表达式体和语句体:允许在Lambda表达式中编写多个语句。
3.引⽤外部变量:Lambda表达式可以引⽤外部作⽤域中的变量。即闭包。
4.异步⽀持:async和await关键字,使得异步编程变得更加容易。Lambda表达式可以与异步⽅法⼀起使⽤,从⽽提供异步操作的简洁语法。
Lambda表达式的应⽤⾮常⼴泛:
1.LINQ查询:Lambda表达式可以作为LINQ查询的条件和投影函数,简化了对集合的筛选、排序和转换操作。
var result =collection.Where(x=>x>5).0rderBy(x => x).Select(x =>x);
2.委托和事件处理:Lambda表达式可以⽤于创建委托实例,或者作为事件处理程序。
Action<int>action=x=>Console.WriteLine(x); button.Click +=(sender,e)=> Console.WriteLine("Button clicked");
3.并⾏编程:通过Parallel类和TaskParallelLibrary(TPL)等并⾏编程框架,Lambda表达式可以⽤于创建并⾏任务和线程。
Parallel.For(0,10,i=>Console.WriteLine(i));Task.Run(()=>{/* 任务代码 */});
4.表达式树:Lambda表达式可以被编译为表达式树,使得我们能够在运⾏时分析和操作代码。
using System;/// <summary>
/// 创建了一个Person对象的列表。
///使用Lambda表达式和LINQ查询来筛选年龄大于30的人。
///Lambda表达式p => p.Age > 30定义了一个匿名函数,
///它接受一个Person对象并返回一个布尔值,表示该对象的年龄是否大于30。
///LINQ查询from p in people where p.Age > 30 select p使用Where子句来筛选满足条件的对象,
///并使用Select子句来选择结果对象。最后,我们使用foreach循环来输出结果。
/// </summary>
public class Person
{public string Name { get; set; }public int Age { get; set; }
}public class Program
{public static void Main(string[] args){List<Person> people = new List<Person>{new Person { Name = "Alice", Age = 30 },new Person { Name = "Bob", Age = 25 },new Person { Name = "Charlie", Age = 35 }};// 使用Lambda表达式筛选年龄大于30的人var adults = people.Where(p => p.Age > 30);// 使用LINQ查询年龄大于30的人var adultsLinq = from p in peoplewhere p.Age > 30select p;// 输出结果foreach (var person in adults){Console.WriteLine($"{person.Name} is an adult.");}foreach (var person in adultsLinq){Console.WriteLine($"{person.Name} is an adult.");}}
}