一、C# 介绍
cloud: Arue
WEB: ASP.NET
C# 刘铁猛学习笔记基础
UP:龙马008
【net6微服务】新手入门,一层一层拨开微服务核心的神秘面纱,让你豁然开朗!服务注册/网关/故障处理/集中鉴权_哔哩哔哩_bilibili
【WPF入门】WPF零基础到精通,从概念到实操,步步提升!_哔哩哔哩_bilibili
新阁教育:
1、各模块介绍与命名空间
2、基本元素
3、方法与调用
4、操作符与表达式
5、数据类型
5.1、字符串
5.2、结构体
在 C# 中,结构体(struct)是一种值类型(value type),用于组织和存储相关数据。
在 C# 中,结构体是值类型数据结构,这样使得一个单一变量可以存储各种数据类型的相关数据。
struct 关键字用于创建结构体
5.3、数组
5.4、集合
5.5、枚举
5.6、列表
5.7、字典
5.8、集合
5.9、泛型
泛型
泛型(Generic) 允许您延迟编写类或方法中的编程元素的数据类型的规范,直到实际在程序中使用它的时候。换句话说,泛型允许您编写一个可以与任何数据类型一起工作的类或方法。
泛型方法
class Program
{
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
static void Main(string[] args)
{
int a, b;
char c, d;
a = 10;
b = 20;
c = 'I';
d = 'V';
// 在交换之前显示值
Console.WriteLine("Int values before calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values before calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
// 调用 swap
Swap<int>(ref a, ref b);
Swap<char>(ref c, ref d);
// 在交换之后显示值
Console.WriteLine("Int values after calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values after calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
Console.ReadKey();
}
}
泛型委托
delegate T NumberChanger<T>(T n);
namespace GenericDelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
// 创建委托实例
NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);
NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);
// 使用委托对象调用方法
nc1(25);
Console.WriteLine("Value of Num: {0}", getNum());
nc2(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
泛型的类型
5.10、常量
public const int a=10; //常量不可更改,必须立刻幅值
常量不可改,不能用类或者结构体类型作为结构体类型
此时可用静态只读字段:
public static readonly Build location new Build("some");
只读:向外暴露不允许更改的数据:只读属性
为了提高程序的可读性和执行效率:常量
为了防止对象的值被改变:只读字段
5.11、值类型与引用类型
值类型:不需要实例化
枚举和结构体为值类型则声明的时候 赋值不需要实例化;
引用类型:
类、委托、数组、字符串、dynamic、接口
- 结构是值类型(Value Type): 结构是值类型,它们在栈上分配内存,而不是在堆上。当将结构实例传递给方法或赋值给另一个变量时,将复制整个结构的内容。
- 类是引用类型(Reference Type): 类是引用类型,它们在堆上分配内存。当将类实例传递给方法或赋值给另一个变量时,实际上是传递引用(内存地址)而不是整个对象的副本。
6、类
这类前边的修饰符:
static
类: 封装修饰符
方法: 封装修饰符+ (virtual或 override)+ 返回值 方法名(传入形参).
委托:delegate,与方法定义类似,前边加了封装类型和delegate修饰
事件:基于委托的事件: 相当于指针
接口:可以实现多继承
6.1、字段
6.2、属性 依赖属性
刘铁猛:C#语言入门详解017字段、属性、索引器、常量_哔哩哔哩_bilibili
基础看刘铁猛:
只读方法:只读属性
class Student
{
private int age ;
public int Age
{
get{return age;} # 无set
}
}
外部不能访问: set加了private
class Student
{
private int age ;
public int Age
{
get{return age;} #
private set{age=value;}
}
}
属性:对内保护字段不被污染,对外;暴露数据,数据可以是存储在字段里的,也可以是动态计算出来的。
6.3 方法
6.3.1、构造方法
6.3.2、析构方法
using System;
namespace LineApplication
{
class Line
{
private double length; // 线条的长度
public Line() // 构造函数
{
Console.WriteLine("对象已创建");
}
~Line() //析构函数
{
Console.WriteLine("对象已删除");
}
public void setLength( double len )
{
length = len;
}
public double getLength()
{
return length;
}
static void Main(string[] args)
{
Line line = new Line();
// 设置线条长度
line.setLength(6.0);
Console.WriteLine("线条的长度: {0}", line.getLength());
}
}
}
6.3.3、扩展方法
扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型
扩展方法必须是静态的、共有的被public static修饰的
必需是形参列表中的第一个,由this修饰;
必需由一个静态类(一般类名为SomeTypeExtension)来统一收纳对SomeType类型的扩展方法;
举例:LINQ方法;
internal class Program
{
static void Main(string[] args)
{
double x = 3.14159;
double y = x.Round(4); // 扩展方法
}
}
static class DoubleExtension
{
public static double Round(this double input,int digits) // 加个this变为扩展方法
{
double result =Math.Round(input ,digits);
return result;
}
}
6.4、封装
访问级别:piblic private ,protected
readonly
一个 访问修饰符 定义了一个类成员的范围和可见性。C# 支持的访问修饰符如下所示:
- public:所有对象都可以访问,同一个解决方案都可访问,实现跨项目访问。
- private:对象本身在对象内部可以访问。
- protected:只有该类对象及其子类对象可以访问,可以跨项目在子类里边调用,无法在子类外调用。
- internal:同一个程序集的对象可以访问,即是同一个项目里都可访问
- protected internal:访问限于当前程序集或派生自包含类的类型。
如果没有指定访问修饰符,则使用类成员的默认访问修饰符,即为 private。
类无修饰符默认为 internal
sealed 、abstract、
6.5 、继承
子类继承父类:全盘继承:字段、属性,方法、嵌套类、委托、事件。构造器不被继承
继承链上的类,先创建父类,再逐级创建子类
子类构造方法与父类构造方法类似,形参和返回值类型和个数一样。
子类不继承父类构造方法的实例:
internal class Program
{
static void Main(string[] args)
{
}
}
class Vehicle
{
public Vehicle(string owner)
{
this.Owner = owner;
}
public string Owner { get; set; }
}
class Car : Vehicle
{
public Car(string owner) : base(owner)
{
this.Owner = owner;
}
public void ShowOwner()
{
Console.WriteLine(Owner);
}
}
可以继承父类方法:包括带返回值和形参的方法
internal class Program
{
public static void Main(string[] args)
{
// 父类可以声明 子类的实例化对象
Vehicle v =new Car();
v.Run();
Car car = new Car();
car.Fuel("123");
}
}
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("I'm running");
//System.out.printIn("I'm running");
}
public void Fuel(string s)
{
Console.WriteLine(s);
}
}
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("I'm Car");
}
}
class RaseCar : Car
{
public void Run()
{
Console.WriteLine("I'm RaseCar");
}
}
在C#中,如果你用父类声明一个变量,并试图用子类去实例化这个变量,那么你只能访问该父类中声明的方法和属性。这个变量在编译时被视为父类类型,因此它只能调用父类中定义的方法和属性。
尽管在运行时这个变量实际上引用的是子类对象,但由于变量是父类类型,你仍然不能通过该变量直接调用子类新增的方法和属性。如果你尝试这样做,编译器会报错,因为它无法确定在父类类型的变量上调用子类特有的方法是否安全。
如果你需要调用子类特有的方法,你需要将该变量显式转换为子类类型。但是,在进行这样的转换之前,你应该检查该变量是否真的引用了一个子类对象,否则你可能会得到一个 InvalidCastException
异常。这通常通过 is
或 as
关键字来实现
class Parent
{
public void ParentMethod()
{
Console.WriteLine("Parent method called.");
}
}
class Child : Parent
{
public void ChildMethod()
{
Console.WriteLine("Child method called.");
}
}
class Program
{
static void Main()
{
Parent parentVar = new Child(); // 父类变量引用子类对象
parentVar.ParentMethod(); // 可以调用父类方法
// parentVar.ChildMethod(); // 编译错误,因为parentVar是父类类型
// 安全转换并调用子类方法
if (parentVar is Child childVar)
{
childVar.ChildMethod(); // 现在可以调用子类方法
}
else
{
Console.WriteLine("parentVar is not a Child instance.");
}
}
}
只有当用IKill声明的new WarmKiller()实例化 才能调用 void IKiller.Kill()方法
internal class Program
{
static void Main(string[] args)
{
// 只有当用IKill声明的new WarmKiller()实例化 才能调用 void IKiller.Kill()方法
IKiller killer = new WarmKiller();
killer.Kill();
}
}
interface IGentleman
{
void Love();
}
interface IKiller
{
void Kill();
}
class WarmKiller:IGentleman,IKiller
{
public void Love()
{
Console.WriteLine("I will love you for ever...");
}
void IKiller.Kill()
{
Console.WriteLine("I'm a Killer");
}
}
6.6、多态
父类对象可以声明子类对象,但是这个变量不能调用子类有父类没有的成员,即智能调用父类中的方法和成员;
多态是同一个行为具有多个不同表现形式或形态的能力。方法重写
多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。
internal class Program
{
static void Main(string[] args)
{
// 父类可以声明 子类的实例化对象
Vehicle v =new Car();
v.Run();
}
}
class Vehicle
{
public virtual void Run()
{
Console.WriteLine("I'm running");
}
}
class Car : Vehicle
{
public override void Run()
{
Console.WriteLine("I'm Car");
}
}
6.7、抽象类和部分类
Abstract 关键字同样可以用来声明抽象方法,抽象方法只包含一个方法名,而没有方法体。
抽象方法没有定义,方法名后面直接跟一个分号,而不是花括号。
1、抽象类
抽象类里边可以定义普通方法和抽象方法:
public abstract class BaseClass
{
public abstract void Eat(); // 抽象方法-吃饭
public abstract void Walk(); // 抽象方法-走路
public abstract void Speak(); // 抽象方法-说话
}
2、部分类
部分类即把类的定义放在多个文件中,如将字段、属性和构造函数放在一个文件中,而把方法放在另一个文件中。
为此,只需在包含部分类定义的每个文件中对类使用partial关键字即可
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
partial class MyClass
{
public void ShowName()
{
Console.WriteLine("姓名: 梦断难寻");
}
}
}
部分类2
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
partial class MyClass
{
public void ShowSex()
{
Console.WriteLine("性别: 男");
}
}
}
在主题方法中使用这个类program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
MyClass My = new MyClass();
My.ShowName();
My.ShowAge();
My.ShowSex();
}
}
}
3、sealed
(1)在类中使用sealed修饰符可防止其他类继承此类
(2)在方法声明中使用sealed修饰符可防止扩充类重写此方法
sealed可用于修饰类、方法、属性。
切记:不能用于修饰字段和变量
被sealed修饰的方法被称为密封方法,所谓密封方法,就是指此方法不能被重写
注意:
(1)sealed修饰方法时,不能单独使用,只有当继承基类时重写基类方法的时候使用,而且,必须与override(重写)关键字一起使用。
(2)切记:只有在继承重写的时候才能使用sealed关键字,如果继承的基类没有你想要被重写的方法,此时使用sealed关键字修饰时错误的
(3) sealed必须重写方法才能使用。也就是说只有基类的方法使用了abstract(抽象的)、virtual(虚拟的)以及override(重写)关键字的时候,才能使用sealed关键字修饰方法
4、注意:
(1)被sealed修饰的类不能使用private、protected……等修饰符,并且不能使用static以及abstract修饰符
(2)被sealed修饰的方法或属性证明是要重写,要与override同用,并且,要与基类的访问修饰符和方法签名一致。注意:当重写时,基类的被重写方法不能使用private、static以及abstract修饰符。
6.8、开闭原则
7、事件、委托、接口
7.1、事件
1、事件 event
作用:为了程序的逻辑更加有道理、更加安全,谨防借刀杀人
事件的本质是委托字段的一个包装器,对委托和字段起限制作用,事件对外界隐藏了委托实例的大部分功能,仅暴露添加/移除事件处理器的功能。
先声明事件:再用+=订阅事件,,声明事件需要先声明
能够发生的事件:事件的订阅
事件多用于桌面、手机等开发的客户端编程,这些程序经常是用户通过事件来驱动的
事件的订阅者,事件的接收者,事件的响应者,事件的处理者,被事件所通知的对象
事件信息、事件消息,事件数据,事件参数
MVC、MVP、MVVM是事件更高级、更高效的”玩法“
事件的拥有者、成员、响应者、
事件的处理器:本质是一个回调方法
事件订阅:将事件处理器与事件关联在一起,本质上是以一种委托类型为基础的约定
在类的内部声明事件,首先必须声明该事件的委托类型
两个事件处理器,
已有事件的订阅
internal class Program
{
static void Main(string[] args)
{
Timer timer =new Timer();
timer.Interval = 1000;
Boy boy = new Boy();
Girl girl = new Girl();
timer.Elapsed += boy.Action; // 订阅事件 +=
timer.Elapsed += girl.Action;
timer.Start();
Console.ReadLine();
}
}
class Boy
{
internal void Action(object sender,ElapsedEventArgs e)
{
Console.WriteLine("Jump!");
}
}
class Girl
{
internal void Action(object sender, ElapsedEventArgs e)
{
Console.WriteLine("Jump112!");
}
}
例二:已有事件的订阅
internal class Program
{
static void Main(string[] args)
{
Form form = new Form();
Controller controller = new Controller(form); // 事件响应者
form.ShowDialog();
}
class Controller
{
private Form form;
public Controller(Form form)
{
if (form != null)
{
this.form = form;
this.form.Click += this.FormClicked ; // 订阅事件
}
}
private void FormClicked(object sender,EventArgs e) // 事件处理器
{
this.form.Text = DateTime.Now.ToString();
}
}
}
internal class Program
{
static void Main(string[] args)
{
MyForm form = new MyForm();
form.Click += form.FormClicked;
form.ShowDialog();
}
}
class MyForm : Form
{
internal void FormClicked(object sender,EventArgs e)
{
this.Text = DateTime.Now.ToString();
}
}
internal class Program
{
static void Main(string[] args)
{
MyForm form = new MyForm();
form.ShowDialog();
}
}
class MyForm : Form
{
private TextBox textBox;
private Button button;
public MyForm()
{
this.textBox = new TextBox();
this.button = new Button();
this.Controls.Add(this.button);
this.Controls.Add(this.textBox);
this.button.Click += this.ButtonClicked;
this.button.Text = "Say Hello";
this.button.Top = 100;
}
private void ButtonClicked(object sender,EventArgs e)
{
this.Text = DateTime.Now.ToString();
this.textBox.Text = "Hello World!!";
}
}
public Form1()
{
InitializeComponent();
// this.myButton.Click += new EventHandler(this.ButtonClicked); // 订阅委托指向的方法
this.myButton.Click += delegate (object Sender, EventArgs e) {
this.mytextBox.Text = "haha"; }; // 此类委托
}
private void ButtonClicked(object sender, EventArgs e)
{
this.mytextBox.Text = "Hello World";
}
上述为已有事件的委托:
2、自定义的事件:
先声明事件:再用+=订阅事件,,声明事件需要先声明,事件定义后可以直接订阅,无需像类和委托那样需要声明一个变量。
能够发生的事件:事件的订阅
using System;
namespace SimpleEvent
{
using System;
/***********发布器类***********/
public class EventTest
{
private int value;
// 声明委托
public delegate void NumManipulationHandler();
public event NumManipulationHandler ChangeNum;
protected virtual void OnNumChanged()
{
if ( ChangeNum != null )
{
ChangeNum(); /* 事件被触发 */
}else {
Console.WriteLine( "event not fire" );
Console.ReadKey(); /* 回车继续 */
}
}
public EventTest()
{
int n = 5;
SetValue( n );
}
public void SetValue( int n )
{
if ( value != n )
{
value = n;
OnNumChanged();
}
}
}
/***********订阅器类***********/
public class subscribEvent
{
public void printf()
{
Console.WriteLine( "event fire" );
Console.ReadKey(); /* 回车继续 */
}
}
/***********触发***********/
public class MainClass
{
public static void Main()
{
EventTest e = new EventTest(); /* 实例化对象,第一次没有触发事件 */
subscribEvent v = new subscribEvent(); /* 实例化对象 */
e.ChangeNum += new EventTest.NumManipulationHandler( v.printf ); /* 注册 */
e.SetValue( 7 );
e.SetValue( 11 );
}
}
}
输出:
event not fire
event fire
event fire
定义的事件:事件基于委托,自定义事件的触发方式
using System;
using System.IO;
namespace BoilerEventAppl
{
// boiler 类
class Boiler
{
private int temp;
private int pressure;
public Boiler(int t, int p)
{
temp = t;
pressure = p;
}
public int getTemp()
{
return temp;
}
public int getPressure()
{
return pressure;
}
}
// 事件发布器
class DelegateBoilerEvent
{
public delegate void BoilerLogHandler(string status);
// 基于上面的委托定义事件
public event BoilerLogHandler BoilerEventLog;
public void LogProcess()
{
string remarks = "O. K";
Boiler b = new Boiler(100, 12);
int t = b.getTemp();
int p = b.getPressure();
if(t > 150 || t < 80 || p < 12 || p > 15)
{
remarks = "Need Maintenance";
}
OnBoilerEventLog("Logging Info:\n");
OnBoilerEventLog("Temparature " + t + "\nPressure: " + p);
OnBoilerEventLog("\nMessage: " + remarks);
}
protected void OnBoilerEventLog(string message)
{
if (BoilerEventLog != null)
{
BoilerEventLog(message); // 事件的触发
}
}
}
// 该类保留写入日志文件的条款
class BoilerInfoLogger
{
FileStream fs;
StreamWriter sw;
public BoilerInfoLogger(string filename)
{
fs = new FileStream(filename, FileMode.Append, FileAccess.Write);
sw = new StreamWriter(fs);
}
public void Logger(string info)
{
sw.WriteLine(info);
}
public void Close()
{
sw.Close();
fs.Close();
}
}
// 事件订阅器
public class RecordBoilerInfo
{
static void Logger(string info)
{
Console.WriteLine(info);
}//end of Logger
static void Main(string[] args)
{
BoilerInfoLogger filelog = new BoilerInfoLogger("e:\\boiler.txt");
DelegateBoilerEvent boilerEvent = new DelegateBoilerEvent();
boilerEvent.BoilerEventLog += new
DelegateBoilerEvent.BoilerLogHandler(Logger);
boilerEvent.BoilerEventLog += new
DelegateBoilerEvent.BoilerLogHandler(filelog.Logger); // 上一行与本行为同一段代码
boilerEvent.LogProcess();
Console.ReadLine();
filelog.Close();
}//end of main
}//end of RecordBoilerInfo
}
例三:
另一种事件的定义: 类似于将委托指向方法
基于委托定义事件: EventHandler微软自定义的委托
声明委托与实例化委托,定义类和实例化类,实例化和定义结构体
internal class Program
{
static void Main(string[] args)
{
Customer customer =new Customer();
Waiter waiter =new Waiter();
customer.Order += waiter.Action; // 添加事件
customer.Order-= waiter.Action; // 移除事件
customer.Action();
customer.PayTheBill();
}
}
public class OrderEventArgs:EventArgs
{
public string DishName { get; set; }
public string Size { get; set; }
}
// 定义委托
public delegate void OrderEventHandler(Customer customer,OrderEventArgs e); // 定义委托
public class Customer
{
// 实例化委托
private OrderEventHandler orderEventHandler; //
// 定义事件
public event OrderEventHandler Order
{
add { this.orderEventHandler += value; }
remove
{
this.orderEventHandler -=value;
}
}
public double Bill { get; set; }
public void PayTheBill()
{
Console.WriteLine("I will pay bill {0}", this.Bill);
}
public void WalkIn()
{
Console.WriteLine("wALK INTO THE RESTAURANT");
}
public void SitDown()
{
Console.WriteLine("Sit down");
}
public void Think()
{
for(int i = 0; i < 5; i++)
{
Console.WriteLine("Let me think...");
Thread.Sleep(1000);
}
if(this.orderEventHandler!= null) // 事件触发
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = "Kongpao Chicken";
e.Size = "large";
this.orderEventHandler.Invoke(this,e); // 事件执行
}
}
public void Action()
{
Console.ReadLine();
this.WalkIn();
this.SitDown();
this.Think();
}
}
public class Waiter
{
public void Action(Customer customer, OrderEventArgs e)
{
Console.WriteLine("I will server you the dish{0}", e.DishName);
double price = 10;
switch (e.Size)
{
case "small":
price = price * 0.5;
break;
case "large":
price = price * 1.5;
break;
default:
break;
}
}
}
另一种定义事件:此时的事件定义(声明)和上述不同
public event OrderEventHandler Order;
protected void OnOrder(string dishName,string size)
{
if (this.Order != null)
{
OrderEventArgs e = new OrderEventArgs();
e.DishName = dishName;
e.Size = size;
this.Order.Invoke(this, e);
}
}
7.2、委托
函数指针升级版:委托指向函数
定义委托,声明委托,实例化委托,定义、声明、实例化类或者结构体
1、Action和Func
用法:Invoke为执行,,执行的两种方式:Invoke或者Invoke去掉,对于Func、Action和delegate自定义的都适用。
同样对于事件的执行也同样适用,具体参见事件:自定义事件的一些例子:例三
internal class Program
{
static void Main(string[] args)
{
Cal1 cal = new Cal1();
Action action = new Action(cal.Report); // 不加括号,加了为调用
action.Invoke(); // 间接调用 ,执行Report这个函数
action(); // 第二种调用方式 执行Report这个函数
// Func委托带返回值
Func<int ,int,int> fun1= new Func<int,int,int>(cal.Add);
int x = 1;
int y = 2;
int z = 0;
// 两种调用方式
z = fun1(x, y);
z = fun1.Invoke(x, y);
}
}
class Cal1
{
public void Report()
{
Console.WriteLine("I have 3methods");
}
public int Add(int a, int b)
{
int result = a + b;
return result;
}
public int Sub(int a, int b)
{
int result = a - b;
return result;
}
}
上述为Action和Func委托,Invoke执行,
Func
Func至少0个输入参数,至多16个输入参数,根据返回值泛型返回。必须有返回值,不可void。
Func<int> 表示没有输入参参,返回值为int类型的委托。
Func<object,string,int> 表示传入参数为object, string ,返回值为int类型的委托。
Func<object,string,int> 表示传入参数为object, string, 返回值为int类型的委托。
Func<T1,T2,,T3,int> 表示传入参数为T1,T2,,T3(泛型),返回值为int类型的委托。
Action
Action委托至少0个参数,至多16个参数,无返回值。
Action 表示无参,无返回值的委托。
Action<int,string> 表示有传入参数int,string无返回值的委托。
Action<int,string,bool> 表示有传入参数int,string,bool无返回值的委托。
Action<int,int,int,int> 表示有传入4个int型参数,无返回值的委托。
委托指向方法:
2、自定义委托
delegate:
当你使用 +=
操作符将一个方法分配给委托时,你实际上是在创建一个委托实例(如果之前不存在的话),并将该实例添加到委托的调用列表中。如果委托已经有一个或多个方法与之关联,那么新添加的方法会被添加到调用列表的末尾,而不是替换现有的方法。
using System;
public delegate void MyDelegate(string message);
public class Program
{
public static void Main()
{
// 创建一个委托变量
MyDelegate myDelegate = null;
// 将方法1分配给委托
myDelegate += Method1;
// 将方法2也分配给同一个委托
myDelegate += Method2;
// 调用委托,这将依次调用方法1和方法2
myDelegate?.Invoke("Hello, world!");
}
// 方法1,与委托签名匹配
public static void Method1(string message)
{
Console.WriteLine("Method 1 called: " + message);
}
// 方法2,也与委托签名匹配
public static void Method2(string message)
{
Console.WriteLine("Method 2 called: " + message);
}
}
需要注意的是,委托是引用类型,因此如果没有将任何方法分配给委托(即委托为 null
),则调用委托(如 myDelegate.Invoke(...)
)将会导致 NullReferenceException
。在上面的例子中,我使用了 myDelegate?.Invoke("Hello, world!")
来安全地调用委托,这样如果委托为 null
,则不会调用任何方法,也不会抛出异常。
委托是一种类,类是数据类型所以委托也是一种数据类型
它的生命方式与一般的类不同,注意委托的位置,别是嵌套类
委托与所封装的方法必需“类型兼容”
参数列表上在个数和数据类型上一致
public delegate double Calc(double x, double y);
internal class Program
{
static void Main(string[] args)
{
Cal1 cal1=new Cal1();
Calc calc1 = new Calc(cal1.Add);
double a = 100;
double b = 200;
double c = 0;
c=calc1.Invoke(a,b);
Console.WriteLine(c);
}
}
class Cal1
{
public void Report()
{
Console.WriteLine("I have 3methods");
}
public double Add(double a, double b)
{
double result = a + b;
return result;
}
public int Sub(int a, int b)
{
int result = a - b;
return result;
}
}
模板方法和回调方法:
分门别类,有利于重复使用,只需要不断增减产品工厂的个数,其他打包方法不用动
// 定义委托
public delegate double Calc(double x, double y);
internal class Program
{
static void Main(string[] args)
{
Cal1 cal1=new Cal1();
Calc calc1 = new Calc(cal1.Add); // 声明和实例化委托
double a = 100;
double b = 200;
double c = 0;
c=calc1.Invoke(a,b);
Console.WriteLine(c);
ProductFactory productFactory = new ProductFactory();
WrapFactory wrapFactory = new WrapFactory();
Func<Product> func1 = new Func<Product>(productFactory.MakePizza); // 绑定一个方法
Func<Product> func2 = new Func<Product>(productFactory.MakeToyCar);
Logger logger = new Logger();
Action<Product> log = new Action<Product>(logger.Log);
Box box1 = wrapFactory.WrapProduct(func1,log); // 传入委托类型形参,传入的是副本,地址
Box box2 = wrapFactory.WrapProduct(func2,log);
Console.WriteLine(box1.Product.Name);
}
}
class Logger
{
public void Log(Product product)
{
Console.WriteLine("Product{0} created at {1}.Price is {2}", product.Name, DateTime.UtcNow, product.Price);
}
}
class Product
{
public string Name { get; set; }
public int Price { get; set; }
}
class Box
{
public Product Product { get; set; } // 属性
}
class WrapFactory
{
public Box WrapProduct(Func<Product> getProduct,Action<Product> logCallback) // 传入的是委托方法,输出为Product的委托指向的方法
{
Box box = new Box();
Product product = getProduct.Invoke();
logCallback.Invoke(product);
box.Product = product;
return box;
}
}
class ProductFactory
{
public Product MakePizza() // 方法
{
Product product = new Product();
product.Name = "Pizza";
product.Price = 12;
return product;
}
public Product MakeToyCar()
{
Product product = new Product();
product.Name = "Toy Car";
product.Price = 200;
return product;
}
}
class Cal1
{
public void Report()
{
Console.WriteLine("I have 3methods");
}
public double Add(double a, double b)
{
double result = a + b;
return result;
}
public int Sub(int a, int b)
{
int result = a - b;
return result;
}
}
委托警示:
internal class Program
{
static void Main(string[] args)
{
Operation opt1 = new Operation();
Operation opt2 = new Operation();
Operation opt3 = new Operation();
opt1.InnerOperation = opt2;
opt2.InnerOperation = opt1;
opt3.Operate(new object(), null, null);
// 问题一:如果传入的两个参数为null,失败和成功的效果是什么?答案: 内层的 操作会调用外层的回调!
// 问题二:如果传入的参数不为null,会出现什么情况? 答案:所有默认callback都被穿透性屏蔽。
}
}
class Operation
{
// Action是C#中的一个预定义委托,它表示一个没有参数且没有返回值(即返回类型为void)的方法
// 定义委托
public Action DefaultSuccessCallback { get; set; }
public Action DefaultFailureCallback { get; set; }
public Operation InnerOperation { get; set; }
public object Operate(object input,Action successCallback,Action failureCallback)
{
if (successCallback == null)
{
successCallback = this.DefaultSuccessCallback;
}
if (failureCallback == null)
{
failureCallback=this.DefaultFailureCallback;
}
object result = null;
try
{
result = this.InnerOperation.Operate(input, successCallback, failureCallback);
}
catch
{
failureCallback.Invoke();
}
successCallback.Invoke();
return result;
}
}
多播委托和异步调用
Invoke是同步调用
BegainInvoke是异步调用
internal class Program
{
static void Main(string[] args)
{
Student stu1 = new Student() { ID = 1, PenColor = ConsoleColor.Yellow };
Student stu2 = new Student() { ID = 2, PenColor = ConsoleColor.Green };
Student stu3 = new Student() { ID = 3, PenColor = ConsoleColor.Red};
Action action1 = new Action(stu1.DoHomework);
Action action2 = new Action(stu2.DoHomework);
Action action3 = new Action(stu3.DoHomework);
// action1.Invoke();
// action2.Invoke();
// action3.Invoke();
// action1 += action2;
// action1 += action3;
//action1.Invoke(); // 多播方法,封装多个委托
// 隐式异步调用,线程加速,下面三个同时运行
//action1.BeginInvoke(null,null);
//action2.BeginInvoke(null, null);
//action3.BeginInvoke(null, null);
// 显示异步调用,,Thread 可换成 Task
Thread thread1 = new Thread(new ThreadStart(stu1.DoHomework));
Thread thread2 = new Thread(new ThreadStart(stu2.DoHomework));
Thread thread3 = new Thread(new ThreadStart(stu3.DoHomework));
thread1.Start();
thread2.Start();
thread3.Start();
}
}
class Student
{
public int ID { get; set; }
public ConsoleColor PenColor { get; set; }
public void DoHomework()
{
for(int i=0; i < 5; i++)
{
Console.ForegroundColor = this.PenColor;
Console.WriteLine("Student{0} doing homework {1} hour(s)",this.ID,i);
Thread.Sleep(1000);
}
}
}
显示异步调用
Invoke是同步调用
BeginInvoke是显示异步调用,Thread和Task,,同时执行
匿名委托
// 使用匿名委托
MyDelegate delegateInstance = delegate (string message) { Console.WriteLine(message); };
delegateInstance("Hello, Anonymous Delegate!");
应用接口取代委托
Lamda表达式委托
// 使用 Lambda 表达式
MyDelegate delegateInstance = message => Console.WriteLine(message);
delegateInstance("Hello, Lambda Delegate!");
7.3、接口
interface:
接口是为解耦而生:“高内聚,低耦合”,方便单元测试;
接口是一个“协约”
接口和抽象类都不能实例化,只能用来声明变量、引用具体类(concrete class)的实例
接口的封装修饰符为public ,所以省略不写;
接口实现自由替换:为解耦而生,接口不实现具体方法,,为继承而用。
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("I'm a boy");
var engine = new Engine();
var car = new Car(engine);
car.Run(3);
Console.WriteLine(car.Speed);
var user= new PhoneUser(new NokiaPhone()); // 可以实现替换
user.UserPhone();
}
}
class PhoneUser
{
private IPhone _phone;
public PhoneUser(IPhone phone)
{
_phone = phone;
}
public void UserPhone()
{
_phone.Dail();
_phone.PickUp();
_phone.Receive();
_phone.Send ();
}
}
interface IPhone
{
void Dail();
void PickUp();
void Send();
void Receive();
}
class NokiaPhone : IPhone
{
public void Dail()
{
Console.WriteLine("Hi This's Tim!");
}
public void PickUp()
{
Console.WriteLine("Hi This's Pick!");
}
public void Receive()
{
Console.WriteLine("Hi This's Receive!");
}
public void Send()
{
Console.WriteLine("Hi This's Send!");
}
}
只有当用IKill声明的new WarmKiller()实例化 才能调用 void IKiller.Kill()方法
internal class Program
{
static void Main(string[] args)
{
// 只有当用IKill声明的new WarmKiller()实例化 才能调用 void IKiller.Kill()方法
IKiller killer = new WarmKiller();
killer.Kill();
}
}
interface IGentleman
{
void Love();
}
interface IKiller
{
void Kill();
}
class WarmKiller:IGentleman,IKiller
{
public void Love()
{
Console.WriteLine("I will love you for ever...");
}
void IKiller.Kill()
{
Console.WriteLine("I'm a Killer");
}
}
强制类型转换 is 或者as 都行
7.4、依赖反转
7.5、依赖注入
1、简介
使用依赖注入,不需要在代码里主动地创建或者获取对象B,相反,只需要在构造器参数里声明需要对象B的引用。
不使用依赖注入的案例:
public class ClassA
{
private readonly ClassB _classB;
public ClassA()
{
_classB = new ClassB(); //主动创建对象B
}
public void Process()
{
_classB.DoSomething();
...
}
}
public class ClassB
{
public void DoSomething()
{
...
}
}
不使用依赖注入,必须在代码的某个地方主动地创建或者获取对象B。
使用依赖注入的:
public class ClassA
{
private readonly ClassB _classB;
public ClassA(ClassB classB) // 声明构造器里需要对象B引用,依赖注入框架就会自动注入对象B
{
_classB = classB;
}
public void Process()
{
_classB.DoSomething();
...
}
}
public class ClassB
{
public void DoSomething()
{
...
}
}
// 伪代码: 向依赖注入系统中注册 ClassA 和 ClassB
DependencyInjectionSystem.AddType(ClassA);
DependencyInjectionSystem.AddType(ClassB);
下述代码使用的是 Microsoft.Extensions.DependencyInjection
命名空间下的依赖注入功能,这是 ASP.NET Core 和 .NET Core 框架中提供的。ServiceCollection
和 IServiceProvider
等类是这些现代框架的核心部分,它们不是 .NET Framework 的一部分。
因此,上述代码不能直接在 .NET Framework 项目中执行。如果您尝试在 .NET Framework 项目中引入 Microsoft.Extensions.DependencyInjection
程序集,您会发现它不兼容,因为这是一个专门为 .NET Core 和 ASP.NET Core 设计的库。
最后,请注意,即使您可以在 .NET Framework 项目中使用依赖注入,但由于框架之间的差异,某些 ASP.NET Core 特定的功能(如中间件、内置的身份验证和授权等)将不可用。因此,在决定如何实施依赖注入时,请考虑您的项目的具体需求和限制。
导入这个引用:using Microsoft.Extensions.DependencyInjection;和安装相应NuGet包
2、依赖注入:
通过这个依赖注入可以将heavyTank改为mediumTank
在 ASP.NET Core 中,可以使用依赖注入容器注册和解析基类类型并注入子类对象
需要把所有设计的依赖关系通过AssScoped注册完成
重点学习依赖注入两种注册方式: 泛型方式和typeof方式
// 第一种
sc.AddScoped(typeof(ITank), typeof(HeavyTank));
// 第二种
sc.AddScoped<ITank, HeavyTank>();
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Security.Authentication.ExtendedProtection;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
namespace _12反射_1
{
internal class Program
{
static void Main(string[] args)
{
var driver = new Driver(new HeavyTank());
driver.Drive();
//
ITank tank = new HeavyTank();
var t = tank.GetType();
object o = Activator.CreateInstance(t);
MethodInfo fireMi = t.GetMethod("Fire");
MethodInfo runMi = t.GetMethod("Run");
fireMi.Invoke(o, null);
runMi.Invoke(o, null);
// 上述
var sc = new ServiceCollection();
sc.AddScoped(typeof(ITank), typeof(HeavyTank));
// var sp = sc.BuildServiceProvider();
// sc.AddScoped<ITank, typeof(HeavyTank>();
// 使用 BuildServiceProvider 方法从 ServiceCollection 构建一个 IServiceProvider 实例
// 此时应用Microsoft.Extensions.DependencyInjection NuGet程序包就可以,安装这个包,BuildServiceProvider就不会报红
//IServiceProvider sp = sc.BuildServiceProvider();
//ITank tank1 =sp.GetService<ITank>();
//tank.Fire();
//tank.Run(); //
// 随意替换体现依赖注入的优点
sc.AddScoped(typeof(IVehicle), typeof(Car));
sc.AddScoped(typeof(Driver)); // 或者另一种注册方式 sc.AddScoped<Driver>();
var sp = sc.BuildServiceProvider();
Driver driver1= sp.GetService<Driver>();
driver1.Drive();
}
}
class Driver
{
private ITank _vehicle;
public Driver(ITank vehicle)
{
_vehicle = vehicle;
}
public void Drive()
{
_vehicle.Run();
}
}
interface IVehicle
{
void Run();
}
class Car : IVehicle
{
public void Run()
{
Console.WriteLine("Car is running...");
}
}
class Truck : IVehicle
{
public void Run()
{
Console.WriteLine("Truck is running...");
}
}
interface IWeapon
{
void Fire();
}
interface ITank:IVehicle,IWeapon
{
void Fire();
void Run();
}
class LightTank : ITank
{
public void Fire()
{
Console.WriteLine("Boom.");
}
public void Run()
{
Console.WriteLine("Ka Ka Kall..");
}
}
class MediumTank : ITank
{
public void Fire()
{
Console.WriteLine("Boom.");
}
public void Run()
{
Console.WriteLine("Ka Ka Kamedium..");
}
}
class HeavyTank : ITank
{
public void Fire()
{
Console.WriteLine("Boom.");
}
public void Run()
{
Console.WriteLine("Ka Ka Kaheavy..");
}
}
}
3、外部dll:
调用外部链接库,并继承
var assembly=AssemblyLoadContext.Default.LoadFromAssemblyPath(file);
var types = assembly.GetTypes();
// See https://aka.ms/new-console-template for more information
using System;
using System.IO;
using System.Collections.Generic;
using System.Runtime.Loader;
namespace BabyStroller.App
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
var folder = Path.Combine(Environment.CurrentDirectory, "animal");
var files = Directory.GetFiles(folder);
var animalTypes=new List<Type>();
foreach (var file in files)
{
var assembly=AssemblyLoadContext.Default.LoadFromAssemblyPath(file);
var types = assembly.GetTypes();
foreach (var t in types)
{
if (t.GetMethod("Voice") != null)
{
animalTypes.Add(t);
}
}
}
while (true)
{
for(int i=0; i < animalTypes.Count; i++)
{
Console.WriteLine($"{i+1}.{animalTypes[i].Name}");
}
Console.WriteLine("======");
Console.WriteLine("Please choose animal");
int index = int.Parse(Console.ReadLine());
if (index > animalTypes.Count || index < 1)
{
Console.WriteLine("No sun an animal.Try again!");
continue;
}
Console.WriteLine("How many times?");
int times = int.Parse(Console.ReadLine());
var t = animalTypes[index - 1];
var m = t.GetMethod("voice");
var o =Activator.CreateInstance(t);
m.Invoke(o, new object[] { times }); // 委托
}
}
}
}
这点难以理解先放着后续再看
7.6、接口隔离
不同功能的接口分开定义,再继承
7.7、单元测试
单元测试 Xunit;
public class UnitTest1
{
[Fact]
public void PowerSupplyLowerThanZero_OK()
{
var fan = new DeskFan(new PowerSupplyLowerThanZero());
var expected = "Won't work";
var actual = fan.Work();
Assert.Equal(expected, actual);
}
}
class PowerSupplyLowerThanZero : IPowerSupply // public 修饰才行
{
public int GetPower()
{
return 0;
}
}
利用Moq资源
public class UnitTest1
{
[Fact]
public void PowerSupplyLowerThanZero_OK()
{
//var fan = new DeskFan(new PowerSupplyLowerThanZero());
//var expected = "Won't work";
// var actual = fan.Work();
//Assert.Equal(expected, actual);
// 单元测试
var mock = new Mock<IPowerSupply>();
mock.Setup(ps=>ps.GetPower()).Returns(() => 0);
var fan = new DeskFan(mock.Object);
var expected = "Won't work";
var actual = fan.Work();
Assert.Equal(expected, actual);
}
}
class PowerSupplyLowerThanZero : IPowerSupply // public 修饰才行
{
public int GetPower()
{
return 0;
}
}
8、特性、反射
8.1、特性
特性(Attribute)是用于在运行时传递程序中各种元素(比如类、方法、结构、枚举、组件等)的行为信息的声明性标签。您可以通过使用特性向程序添加声明性信息。一个声明性标签是通过放置在它所应用的元素前面的方括号([ ])来描述的。
特性(Attribute)用于添加元数据,如编译器指令和注释、描述、方法、类等其他信息。.Net 框架提供了两种类型的特性:预定义特性和自定义特性。
Obosolete 声明此方法
Conditional 定义是否调用,上边定义一个宏,,# define
internal class Program
{
static void Main(string[] args)
{
var driver = new Driver(new HeavyTank());
driver.Drive();
//
ITank tank = new HeavyTank();
var t = tank.GetType();
object o = Activator.CreateInstance(t);
MethodInfo fireMi = t.GetMethod("Fire");
MethodInfo runMi = t.GetMethod("Run");
fireMi.Invoke(o, null);
runMi.Invoke(o, null);
// 上述
var sc = new ServiceCollection();
sc.AddScoped(typeof(ITank), typeof(HeavyTank));
// var sp = sc.BuildServiceProvider();
// 使用 BuildServiceProvider 方法从 ServiceCollection 构建一个 IServiceProvider 实例
// 此时应用Microsoft.Extensions.DependencyInjection NuGet程序包就可以,安装这个包,BuildServiceProvider就不会报红
IServiceProvider serviceProvider = sc.BuildServiceProvider();
}
}
class Driver
{
private ITank _vehicle;
public Driver(ITank vehicle)
{
_vehicle = vehicle;
}
public void Drive()
{
_vehicle.Run();
}
}
interface IVehicle
{
void Run();
}
class Car : IVehicle
{
public void Run()
{
Console.WriteLine("Car is running...");
}
}
class Truck : IVehicle
{
public void Run()
{
Console.WriteLine("Truck is running...");
}
}
interface IWeapon
{
void Fire();
}
interface ITank:IVehicle,IWeapon
{
void Fire();
void Run();
}
class LightTank : ITank
{
public void Fire()
{
Console.WriteLine("Boom.");
}
public void Run()
{
Console.WriteLine("Ka Ka Ka..");
}
}
class MediumTank : ITank
{
public void Fire()
{
Console.WriteLine("Boom.");
}
public void Run()
{
Console.WriteLine("Ka Ka Ka..");
}
}
class HeavyTank : ITank
{
public void Fire()
{
Console.WriteLine("Boom.");
}
public void Run()
{
Console.WriteLine("Ka Ka Ka..");
}
}
8.2、反射
反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
程序集包含模块,而模块包含类型,类型又包含成员。反射则提供了封装程序集、模块和类型的对象。
您可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性
9.、综合
lamda Linq
9.1、泛型
1、方法泛型:
代码:
static void Swap<T>(ref T lhs, ref T rhs)
{
T temp;
temp = lhs;
lhs = rhs;
rhs = temp;
}
static void Main(string[] args)
{
int a, b;
char c, d;
a = 10;
b = 20;
c = 'I';
d = 'V';
// 在交换之前显示值
Console.WriteLine("Int values before calling swap:");
Console.WriteLine("a = {0}, b = {1}", a, b);
Console.WriteLine("Char values before calling swap:");
Console.WriteLine("c = {0}, d = {1}", c, d);
// 调用 swap
Swap<int>(ref a, ref b);
Swap<char>(ref c, ref d);
2、委托泛型:
delegate T NumberChanger<T>(T n);
namespace GenericDelegateAppl
{
class TestDelegate
{
static int num = 10;
public static int AddNum(int p)
{
num += p;
return num;
}
public static int MultNum(int q)
{
num *= q;
return num;
}
public static int getNum()
{
return num;
}
static void Main(string[] args)
{
// 创建委托实例
NumberChanger<int> nc1 = new NumberChanger<int>(AddNum);
NumberChanger<int> nc2 = new NumberChanger<int>(MultNum);
// 使用委托对象调用方法
nc1(25);
Console.WriteLine("Value of Num: {0}", getNum());
nc2(5);
Console.WriteLine("Value of Num: {0}", getNum());
Console.ReadKey();
}
}
}
9.2、索引器
用来检索集合
1、语法:
element-type this[int index]
{
// get 访问器
get
{
// 返回 index 指定的值
}
// set 访问器
set
{
// 设置 index 指定的值
}
}
2、列表实例:
using System;
namespace IndexerApplication
{
class IndexedNames
{
private string[] namelist = new string[size];
static public int size = 10;
public IndexedNames()
{
for (int i = 0; i < size; i++)
namelist[i] = "N. A.";
}
public string this[int index]
{
get
{
string tmp;
if( index >= 0 && index <= size-1 )
{
tmp = namelist[index];
}
else
{
tmp = "";
}
return ( tmp );
}
set
{
if( index >= 0 && index <= size-1 )
{
namelist[index] = value;
}
}
}
static void Main(string[] args)
{
IndexedNames names = new IndexedNames();
names[0] = "Zara";
names[1] = "Riz";
names[2] = "Nuha";
names[3] = "Asif";
names[4] = "Davinder";
names[5] = "Sunil";
names[6] = "Rubic";
for ( int i = 0; i < IndexedNames.size; i++ )
{
Console.WriteLine(names[i]);
}
Console.ReadKey();
}
}
}
3、字典实例索引器:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace _01索引器
{
internal class Program
{
static void Main(string[] args)
{
Student stu =new Student();
var mathscore = stu["123"];
}
}
class Student // 索引器
{
private Dictionary<string,int> scoreDictionary= new Dictionary<string,int>();
public int? this[string subject]
{
get
{
if(this.scoreDictionary.ContainsKey(subject))
{
return this.scoreDictionary[subject];
}
else
{
return null;
}
}
set
{
// 检测是否有值
if(value.HasValue==false)
{
throw new Exception("Score cannot be null ");
}
if (this.scoreDictionary.ContainsKey(subject))
{
this.scoreDictionary[subject] = value.Value;
}
else
{
this.scoreDictionary.Add(subject, value.Value);
}
}
}
}
}
9.3、方法形参
C#语言入门详解018传值 输出 引用 数组 具名 可选参数,扩展方法_哔哩哔哩_bilibili
引用类型:
1、值参数
声明时不带修饰符的形参是值形参,一个值形参对应于一个局部变量,
值参数创建变量的副本,复制
值类型传值参数
引用类型传值参数
被赋予新值: 值参数创建副本不影响外部变量的值
internal class Program
{
static void Main(string[] args)
{
Student1 stu =new Student1() { Name="Tom"};
SomeMethod(stu);
Console.WriteLine(stu.Name); // 输出Tom
}
static void SomeMethod(Student1 stu)
{
stu = new Student1() { Name = "Tim" }; // 实例化一个新地址
Console.WriteLine(stu.Name); // 输出 Tim
}
}
class Student1{ public string Name{ get;set;}}
b被修改:引用类型不创建新的存储位置
internal class Program
{
static void Main(string[] args)
{
Student1 stu =new Student1() { Name="Tom"};
SomeMethod(stu);
Console.WriteLine(stu.Name); // 输出Tim
}
static void SomeMethod(Student1 stu) // 传进来的是地址
{
stu.Name = "Tim";
Console.WriteLine(stu.Name); // 输出 Tim
}
}
class Student1{ public string Name{ get;set;}}
2、out ref
ref引用形参
引用参数不创建变量的副本,引用参数还是那个堆地址,和值参数创建副本不一样
static void Main(string[] args)
{
int a = 1;
Add(ref a);
Console.WriteLine(a); // a=2
}
static void Add(ref int a)
{
a = a + 1;
}
ref 传入的是地址,ref 修饰符显示指出改变实际参数的值
地址和值均改变了,
internal class Program
{
static void Main(string[] args)
{
Student1 outstu =new Student1() { Name="Tom"};
Console.WriteLine(outstu.Name); // 输出Tom
SomeMethod(ref outstu);
// 下面输出地址和值均改变了,值为Tim
Console.WriteLine("HashCode{0},{1}",outstu.GetHashCode(),outstu.Name);
}
static void SomeMethod(ref Student1 stu)
{
stu = new Student1() { Name = "Tim" }; // 实例化一个新地址
Console.WriteLine(stu.Name); // 输出 Tim
}
}
class Student1{ public string Name{ get;set;}}
引用类型,存储的是一个地址,通过地址取索引值,参数和变量指向同一个地址,堆内存上的地址,堆上的值改变,变量的地址改变 堆和栈,地址
ref修饰传入形参后,引用类型与传入形参
实例:验证引用类型与值类型的引用形参的区别
new引用类型,,实例化时创建一种新地址以及新变量,新地址存储,,ref 变量不生成副本,还是同一个,指向是一样的,但是只想里边的地址, ref改变引用形参
static void Main(string[] args)
{
Student1 outstu = new Student1() { Name = "Tom" };
Console.WriteLine("{0},{1}", outstu.GetHashCode(), outstu.Name);
SomeMethod(outstu);
Console.WriteLine(outstu.Name);
int a = 1;
Add(ref a);
Console.WriteLine("{0},{1}",outstu.GetHashCode(),outstu.Name);
}
static void SomeMethod(Student1 stu)
{
stu = new Student1() { Name = "Tim" };
//stu.Name = "Tom";
Console.WriteLine("{0},{1}", stu.GetHashCode(), stu.Name);
}
static void Add(ref int a)
{
a = a + 1;
// Console.WriteLine("{0},{1}", a.GetHashCode(), a);
}
class Student1 { public string Name { get; set; } }
out输出形参
输出形参并不创建变量的副本
方法体内必须要有对输出变量的赋值的操作
适用out修饰符显式指出-副作用时通过参数向外输出值
变量在作为输出形参传递之前可以不一定需要明确赋值,
但是在方法返回之前必须赋值
值类型
static void Main(string[] args)
{
Console.WriteLine("please input first number:");
string arg = Console.ReadLine();
double x = 0;
bool b1 = double.TryParse(arg, out x);
Console.WriteLine(x);
if (b1 == false)
{
Console.WriteLine("input error");
return;
}
Console.WriteLine("please input first number:");
string arg1 = Console.ReadLine();
double y= 0;
bool b2 = double.TryParse(arg1, out y);
Console.WriteLine(y);
if (b2 == false)
{
Console.WriteLine("input error");
return;
}
}
输出形参,out引用类型的,不创建副本
class Student
{
public int Age { get; set; }
public string Name { get; set; }
}
class StudentFactory
{
public static bool Create(string stuName,int stuAge,out Student result)
{
result = null;
if (string.IsNullOrEmpty(stuName))
{
return false;
}
if (stuAge < 20 || stuAge > 80)
{
return false;
}
result = new Student(){ Age = stuAge, Name = stuName };
return true;
}
不创建副本
3、形参数组
params修饰符
static void Main(string[] args)
{
string str = "Tim;Tom,Amy.Lisa";
str.Split(';', ',', '.');
int result = CalSun(1, 2, 3);
Console.WriteLine(result);
}
static int CalSun(params int[] intarray)
{
int sum = 0;
foreach (var item in intarray)
{
sum += item;
}
return sum;
}
只能有一个params参数
必须是形参列表中的最后一个方法,由params修饰,String.Format方法和String.Split方法;
具名参数
static void Main(string[] args)
{
string str = "Tim;Tom,Amy.Lisa";
str.Split(';', ',', '.');
int result = CalSun(1, 2, 3);
Console.WriteLine(result);
PrintInfo(age: 34, name: "Tim"); // 具名参数
}
static int CalSun(params int[] intarray)
{
int sum = 0;
foreach (var item in intarray)
{
sum += item;
}
return sum;
}
static void PrintInfo(string name,int age)
{
Console.WriteLine("Hello{0},you are {1}", name, age);
}
可选参数
static string GetStr(string s = "a", int i = 10, string r = "rrrr")
{
return s + i + r;
}
调用方法:
GetStr();
GetStr("abcde");
GetStr("abcde", 100);
GetStr("abcde", 100, "hjklmn");
9.4、lamda表达式
1、简介
(输入参数)=>
2、异步Lamda
通过使用 async 和 await 关键字,你可以轻松创建包含异步处理的 lambda 表达式和语句。其中async
和 await
关键字是在 C# 5 中引入的。
9.5、匿名方法
匿名方法(Anonymous methods) 提供了一种传递代码块作为委托参数的技术。匿名方法是没有名称只有主体的方法。
在匿名方法中您不需要指定返回类型,它是从方法主体内的 return 语句推断的。
using System;
delegate void NumberChanger(int n);
namespace DelegateAppl
{
class TestDelegate
{
static int num = 10;
public static void AddNum(int p)
{
num += p;
Console.WriteLine("Named Method: {0}", num);
}
public static void MultNum(int q)
{
num *= q;
Console.WriteLine("Named Method: {0}", num);
}
static void Main(string[] args)
{
// 使用匿名方法创建委托实例
NumberChanger nc = delegate(int x)
{
Console.WriteLine("Anonymous Method: {0}", x);
};
// 使用匿名方法调用委托
nc(10);
// 使用命名方法实例化委托
nc = new NumberChanger(AddNum);
// 使用命名方法调用委托
nc(5);
// 使用另一个命名方法实例化委托
nc = new NumberChanger(MultNum);
// 使用命名方法调用委托
nc(2);
Console.ReadKey();
}
}
}