文章目录
特性与反射
特性
.net有三种预定义特性
- Conditional(“条件”):条件标记特性,满足条件才能编译
- Obsolete(“输出信息”, bool):过时标记特性,如果为假则生成警告,如果为真则输出错误;默认为假;
- AttributeUsage(AttributeTarget, AllowMultiple, Inherited):自定义标记特性
- AttributeTarget:枚举类型AttributeTargets的组合,默认AttributeTargets.All,表示该特性可以放在哪些东西前面
- AllowMultiple:布尔值,该特性是否可以多用
- Inherited:布尔值,该特性是否可以继承
反射
- 在运行时查看某个类的相关特性
- (其他的暂时没有学习)
using System;
using System.Reflection;
namespace MySpace
{
public class TestConditional
{
[Conditional("RELEASE")] // 条件编译:当编译环境为Release时才会编译releaseMessage函数,而debugMessage函数不受影响
public static void releaseMessage(string msg)
{
Console.WriteLine(msg);
}
public static void debugMessage(string msg)
{
Console.WriteLine(msg);
}
}
public class TestObsolete
{
[Obsolete("Don't use OldMethod, use NewMethod instead", true)] // Old方法如果被使用,则出错,并输出信息
public static void OldMethod()
{
Console.WriteLine("It is the old method");
}
public static void NewMethod()
{
Console.WriteLine("It is the new method");
}
}
[AttributeUsage(AttributeTargets.Class | // 该特性能够被放在类、构造器、域、方法、特性前面
AttributeTargets.Constructor |
AttributeTargets.Field |
AttributeTargets.Method |
AttributeTargets.Property,
AllowMultiple = true, // 该特性可多用
Inherited = true)] // 该特性可继承
public class DeBugInfo : System.Attribute
{
private int bugNo;
private string developer;
private string lastReview;
public string message;
public DeBugInfo(int bg, string dev, string d)
{
this.bugNo = bg;
this.developer = dev;
this.lastReview = d;
}
public int BugNo
{
get
{
return bugNo;
}
}
public string Developer
{
get
{
return developer;
}
}
public string LastReview
{
get
{
return lastReview;
}
}
public string Message
{
get
{
return message;
}
set
{
message = value;
}
}
}
[DeBugInfo(45, "Zara Ali", "12/8/2012", Message = "Return type mismatch")]
[DeBugInfo(49, "Nuha Ali", "10/10/2012", Message = "Unused variable")]
class Rectangle
{
// 成员变量
protected double length;
protected double width;
public Rectangle(double l, double w)
{
length = l;
width = w;
}
[DeBugInfo(55, "Zara Ali", "19/10/2012",
Message = "Return type mismatch")]
public double GetArea()
{
return length * width;
}
[DeBugInfo(56, "Zara Ali", "19/10/2012")]
public void Display()
{
Console.WriteLine("Length: {0}", length);
Console.WriteLine("Width: {0}", width);
Console.WriteLine("Area: {0}", GetArea());
}
}
class Test
{
public static void Main()
{
TestConditional.releaseMessage("当前处于Release");
TestConditional.debugMessage("当前处于Dedug");
TestObsolete.NewMethod();
// TestObsolete.OldMethod();
Rectangle r = new Rectangle(4.5, 7.5);
r.Display();
Type type = typeof(Rectangle);
// 遍历类特性
foreach (Object attributes in type.GetCustomAttributes(false))
{
DeBugInfo dbi = (DeBugInfo)attributes;
if (null != dbi)
{
Console.WriteLine("Bug no: {0}", dbi.BugNo);
Console.WriteLine("Developer: {0}", dbi.Developer);
Console.WriteLine("Last Reviewed: {0}",
dbi.LastReview);
Console.WriteLine("Remarks: {0}", dbi.Message);
}
}
// 遍历方法特性
foreach (MethodInfo m in type.GetMethods())
{
foreach (Attribute a in m.GetCustomAttributes(true))
{
DeBugInfo dbi = a as DeBugInfo;
if (null != dbi)
{
Console.WriteLine("Bug no: {0}, for Method: {1}",
dbi.BugNo, m.Name);
Console.WriteLine("Developer: {0}", dbi.Developer);
Console.WriteLine("Last Reviewed: {0}",
dbi.LastReview);
Console.WriteLine("Remarks: {0}", dbi.Message);
}
}
}
Console.ReadLine();
}
}
}
属性和索引器
属性
对象的成员变量一般都是私有的,如果想访问和修改某一对象的成员变量,可以为其添加一个相对应的属性,然后通过属性来访问和修改
class Rectangle
{
// 成员变量,无法直接访问
private int length;
// 属性
// 设置方法1
public int Length
{
get => length;
set => length = value;
}
// 设置方法2
public int Length
{
get{ return length; }
set{ length = value; }
}
}
索引器
可以看作对属性的扩充,它使得访问和修改对象的某个私有数组变量时,可以对该对象直接以下标方式访问和修改
class SchoolClass
{
private string[] names;
private int size;
public SchoolClass(int n)
{
size = n;
names = new string[10];
}
public string this[int index]
{
get
{
string tmp;
if( index >= 0 && index <= size-1 )
{
tmp = names[index];
}
else
{
tmp = "";
}
return tmp;
}
set
{
if( index >= 0 && index <= size-1 )
{
names[index] = value;
}
}
}
}
SchoolClass class1 = new SchoolClass(10);
class1[0] = "XiaoMing";
Console.WriteLine(class1[0]);
委托与事件
委托
相当于C++中的函数指针,使用**delegate
**关键字声明,有注意有作用域、返回值和参数类型,且返回值和参数类型要和要调用的函数相同
public int print1(int n)
{
Console.WriteLine($"print1输出{n}");
return 1;
}
public int print2(int n)
{
Console.WriteLine("print2输出{n}");
return 2;
}
// 委托声明
public delegate int MyDelegate(int n);
// 委托实例化
MyDelegate MD1 = new MyDelegate(print1); // print1不写参数只写函数名
MyDelegate MD2 = new MyDelegate(print2);
// 普通使用
MD1(1); // 输出“print1输出1”
MD2(2); // 输出“print2输出2”
// 多播使用
MyDelegate MD;
MD = MD1 + MD2; // 多播:此时形成了一个调用方法的列表
MD(3); // 输出“print1输出3”,再输出“print2输出3”
MD -= MD2; // 减去列表中的MD2
MD(3); // 输出“print1输出3”
Action委托
这是委托的一种实现形式,不必提前声明委托而可以直接绑定委托和方法,它主要用于有参而无返回值的方法
// 实例化:有参方法
Action<int, string, ...> MyDelegate = new Action<int, string, ...>(MyFunction); // void MyFunction(int, string, ...)
// 使用
MyDelegate(1, "LiMing");
// 实例化:无参方法
Action MyDelegate = new Action(MyFunction); // void MyFunction(void)
MyDelegate();
Func委托
这是委托的第二种实现形式,也是不必提前声明委托而可以直接绑定委托和方法,它主要用于有参且有返回值的方法
// 实例化:有参方法
Func<int, string, ...> MyDelegate = new Func<int, string, ...>(MyFunction); // string MyFunction(int, string, ...)
// 使用
Console.WriteLine(MyDelegate(1, "LiMing"));
// 实例化:无参方法
Func MyDelegate = new Func(MyFunction); // string MyFunction(void)
// 使用
Console.WriteLine(MyDelegate());
事件
事件用**event
**关键字配合委托进行声明,它由发布者发布,订阅者进行订阅。发布者定义一个事件,当事件发生时,订阅者的事件处理函数对该事件进行处理。
- 发布者包含了事件和处理事件的委托,订阅者是一个普通的类。
- 订阅者要想成功订阅,则它处理事件的方法必须能被发布者处理事件的委托成功委托,即参数和返回值相同。
class Publisher
{
private num = 10;
private original_num = 10;
// 先声明委托:用来将处理事件的方法进行委托
public delegate void Handler();
// 再根据委托声明事件
public event Handler Event;
// 在num发生改变时,发布事件Event
private void IsChange()
{
if(num != original_num)
Event();
}
// 方法
public void SetValue(int val)
{
num = val;
IsChange();
}
}
class Subscriber
{
public void Print()
{
Console.WriteLine($"发布者的值已改变")
}
}
// 实例化
Publisher pub = new Publisher(); // 发布者里面定义了事件、委托
Subscriber sub = new Subscriber(); // 订阅者只是个单纯的类,没有事件与委托
// 注册:关联实例化对象的委托与事件
pub.Event += new Publisher.Handler(subscriber.print); //注意:前面是发布者对象的Event事件,后面是发布者“类”的委托
// 测试
pub.SetValue(10); // 未触发事件
pub.SetValue(20); // 事件(对象pub的Event)被触发,传递给了委托(类Publisher的Hander),进而又传递给了处理方法(对象subscriber的Print):输出“发布者的值已改变”
进程与线程
进程
在C#中通过using System.Diagnostics
来引入进程类Progress
。我们使用进程类来启动某一可执行程序,此时该程序与主程序并发执行。
using System.Diagnostics;
/********************************普通方法************************************/
// 实例化一个空“进程”对象
Progress MyProgress = new Progress();
// 给它写入相关信息
process.StartInfo.FileName = Application.StartupPath + @"\python.exe";
process.StartInfo.Arguments = "1 2 3"; // test.exe的相关参数,多个参数则用空格隔开
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
// 启动
process.Start();
/*************************异步回调方法(进程输出结果事件发生时,引起回调并处理)****************************/
// 声明一个“进程”
Process MyProcess;
// 实例化一个“进程开始信息”对象
ProcessStartInfo startInfo = new ProcessStartInfo
{
FileName = @"python.exe",
Arguments = "1 2 3",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
// 用“进程开始信息”实例化一个匿名“进程”对象并启动,然后将该匿名对象返回给MyProcess(引用)
using (MyProcess = Process.Start(startInfo))
{
// 开始不断输出命令行内容
MyProcess.BeginOutputReadLine();
// 注册:当事件MyProcess.OutputDataReceived被发布者发布时,触发订阅者处理函数subscriber.addressFunction()执行
MyProcess.OutputDataReceived += new DataReceivedEventHandler(subscriber.addressFunction);
}
/*********************订阅者subscriber****************************/
public void addressFunction(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
// ...
}
}
线程
在C#中通过using System.Threading.Thread
来引入线程类Thread
。我们使用线程类来调用某一方法,此时该方法与主方法并发执行。
无参方法
使用委托ThreadStart
来调用无参方法
void MyFunction()
{
Console.WriteLine("Ok");
}
/***********************使用*****************************/
Thread thread_MyFunction = new Thread(new ThreadStart(MyFunction));
thread_MyFunction.Name = "MyFunction";
thread_MyFunction.Start();
有参方法
使用委托ParameterizedThreadStart
来调用有参方法
- 有参方法中只能有一个
Object
参数 - 想要传递多个参数,可以使用集合、结构体、类来容纳多个参数,然后利用装箱和拆箱来处理
void MyFunction(Object obj)
{
ArrayList list = obj as ArrayList; // Object类对象拆箱还原为ArrayList类
for(int i = 0; i < list.Count; ++i)
{
Console.WriteLine(list[i]);
}
}
ArrayList list = new ArrayList();
list.Add("A");
list.Add("B");
list.Add("C");
list.Add(1);
list.Add(2);
list.Add(3);
/*************************使用*************************************/
Thread thread_MyFunction = new Thread(new ParameterizedThreadStart(MyFunction));
thread_MyFunction.Name = "MyFunction";
thread_MyFunction.Start(list); // 启动:list装箱为Object类对象
异步编程
基于任务的异步模式 (TAP)
基于任务的异步编程核心是Task
和 Task<T>
对象,这两个对象对异步操作建模
**async
修饰符和await
**关键字
-
async
修饰符:写在包含异步和await
语句的方法名前,如public async Task Function(){}
,用于通知编译器进行异步处理 -
await
关键字:等待异步任务完成结果,如int ResultValue = await Calculate(10)
,此时启动了Calculate任务,然后关注着任务的完成结果,如果此时有其他任务,则同时关注多个任务的完成结果- 带有
await
的语句是假阻塞式运行的。如果存在多个异步任务,普通语句在执行时会阻塞式运行,中断其他异步任务,直到完成后才继续其他任务并执行下一条语句;而带有await
的语句不会,虽然程序会卡在这条语句上,但其他异步任务依然还是会执行的(所以我称它为假阻塞) - 要注意,
await
关键字关注的是任务的结果,而不是具体过程
- 带有
-
原则
- 在一个异步任务中,如果包含其他费时间的方法的调用,也应该采用异步方式去调用
- 尽可能先启动任务,不要在等待任务完成时造成阻塞
- 将多个同类型的同步操作组合到一个方法中,然后异步启动它
static async void Main() { // 异步启动任务 Task<int> tskA = Add(1, 2); // Add任务开始 Task<int> tskB = Divide(10, 2); // Divide任务紧接着开始 // ... // 两个任务并发执行 // ... // 等待任务完成结果 int ResultA = await tskA; // 如果Divide任务先完成,也会卡在这一句,只有等Add任务完成后,tskA结果到达并赋给变量ResultA后,才会执行下一句 int ResultB = await tskB; } static Task<int> Add(a, b) // 返回一个类型为int的Task结果 { num = a + b; return num; } static async Task<int> Divide(a, b) // 返回一个类型为int的Task结果,因为里面有异步场景,所以要用async修饰 { bool v = await IsValid(a, b); // 异步方式调用 if(v == true) num = a / b; else return 0; } static bool IsValid(a, b) // 返回一个bool结果 { if(b == 0) return false; else return true; }
善用Task的API
-
Task.WhenAll()
等待括号中的所有任务都执行完成,返回任务**完成情况的结果(**不是任务的结果)
var eggsTask = FryEggsAsync(2); var baconTask = FryBaconAsync(3); var toastTask = MakeToastWithButterAndJamAsync(2); await Task.WhenAll(eggsTask, baconTask, toastTask); Console.WriteLine("Eggs are ready"); Console.WriteLine("Bacon is ready"); Console.WriteLine("Toast is ready"); Console.WriteLine("Breakfast is ready!");
-
Task.WhenAny()
表示提供的任务之一已完成的任务, 返回任务的结果是完成的结果
using System; using System.Collections.Generic; using System.Threading.Tasks; namespace AsyncBreakfast { class Program { static async Task Main(string[] args) { Coffee cup = PourCoffee(); Console.WriteLine("coffee is ready"); var eggsTask = FryEggsAsync(2); var baconTask = FryBaconAsync(3); var toastTask = MakeToastWithButterAndJamAsync(2); // 使用 var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask }; while (breakfastTasks.Count > 0) { Task finishedTask = await Task.WhenAny(breakfastTasks); if (finishedTask == eggsTask) { Console.WriteLine("eggs are ready"); } else if (finishedTask == baconTask) { Console.WriteLine("bacon is ready"); } else if (finishedTask == toastTask) { Console.WriteLine("toast is ready"); } breakfastTasks.Remove(finishedTask); } Juice oj = PourOJ(); Console.WriteLine("oj is ready"); Console.WriteLine("Breakfast is ready!"); } static async Task<Toast> MakeToastWithButterAndJamAsync(int number) { var toast = await ToastBreadAsync(number); ApplyButter(toast); ApplyJam(toast); return toast; } private static Juice PourOJ() { Console.WriteLine("Pouring orange juice"); return new Juice(); } private static void ApplyJam(Toast toast) => Console.WriteLine("Putting jam on the toast"); private static void ApplyButter(Toast toast) => Console.WriteLine("Putting butter on the toast"); private static async Task<Toast> ToastBreadAsync(int slices) { for (int slice = 0; slice < slices; slice++) { Console.WriteLine("Putting a slice of bread in the toaster"); } Console.WriteLine("Start toasting..."); await Task.Delay(3000); Console.WriteLine("Remove toast from toaster"); return new Toast(); } private static async Task<Bacon> FryBaconAsync(int slices) { Console.WriteLine($"putting {slices} slices of bacon in the pan"); Console.WriteLine("cooking first side of bacon..."); await Task.Delay(3000); for (int slice = 0; slice < slices; slice++) { Console.WriteLine("flipping a slice of bacon"); } Console.WriteLine("cooking the second side of bacon..."); await Task.Delay(3000); Console.WriteLine("Put bacon on plate"); return new Bacon(); } private static async Task<Egg> FryEggsAsync(int howMany) { Console.WriteLine("Warming the egg pan..."); await Task.Delay(3000); Console.WriteLine($"cracking {howMany} eggs"); Console.WriteLine("cooking the eggs ..."); await Task.Delay(3000); Console.WriteLine("Put eggs on plate"); return new Egg(); } private static Coffee PourCoffee() { Console.WriteLine("Pouring coffee"); return new Coffee(); } } }
异步编程模型模式(APM):不建议使用
.net为自定义委托提供了三个方法
MyDelegate.Invoke(param1, param2, ..., )
:相当于直接调用委托,即**MyDelegate(param1, param2, ...)
**,这是阻塞式的IAsyncResult BeginInvoke(param1, param2, ..., AsyncResult callback, Object o)
:非阻塞式开始调用委托,程序继续向下执行,一直到EndInvoke
语句string EndInvoke(IAsyncResult result)
:等待IAsyncResult结果
此外有一个接口和一个委托可用来配合异步编程:
-
**
IAysncResult
**接口:监视和管理异步操作public interface IAsyncResult { Object AsyncState { get; } // 该属性为BeginInvoke参数中的最后一个参数对象 WaitHandle AsyncWaitHandle { get; } bool CompletedSynchronously { get; } // 如果开始操作调用已同步完成,则其属性将被设置为 true。 bool IsCompleted { get; } // 该属性判断异步调用是否结束 }
-
AsyncCallback
委托 : 用于指定在开始操作完成后应被调用的方法(异步回调时使用)
APM不支持对异步操作的取消、且没有提供对进度报告的功能,但基于任务的异步模式(TAP)和基于事件的异步模式(EAP)支持
同步调用
class Test
{
// 委托声明
public delegate int MyDelegate(int n);
// 函数
public int print(int n)
{
Console.WriteLine($"print输出{n}");
return true;
}
static void Main()
{
// 委托实例化
MyDelegate MD = new MyDelegate(print);
// 同步调用
MD.Invoke(1); // 相当于MD(1)
console.WriteLine("完成"); // 只有当MD.Invoke(1)执行完成后才会执行这一句
}
}
异步调用
class Test
{
// 委托声明
public delegate string MyDelegate(int n);
// 函数
public string print(int n)
{
Console.WriteLine($"print输出{n}");
return "Ok";
}
static void Main()
{
// 委托实例化
MyDelegate MD = new MyDelegate(print);
// 异步调用
IAsyncResult result = MD.BeginInvoke(1, null, null); // 启动调用,并继续向下执行,result监视异步操作
// ...
console.WriteLine("正在执行");
// ...
string str = MD.EndInvoke(result); // 阻塞式等待结果
console.WriteLine("完成"); // 只有当MD.EndInvoke(result)执行完成后才会执行这一句
console.WriteLine(str);
}
}
异步回调
class Test
{
// 委托声明
public delegate string MyDelegate(int n);
// 函数
public string print(int n)
{
Console.WriteLine($"print输出{n}");
return "Ok";
}
// 回调函数
public void CallbackFunction(IAsyncResult result)
{
//AsyncResult 是IAsyncResult接口的一个实现类,空间:System.Runtime.Remoting.Messaging
//AsyncDelegate 属性可以强制转换为用户定义的委托的实际类
MyDelegate MD = (MyDelegate)((AsyncResult)result).AsyncDelegate;
Console.WriteLine(MD.EndInvoke(result));
Console.WriteLine(MD.AsyncState);
}
static void Main()
{
// 委托实例化
MyDelegate MD = new MyDelegate(print);
// 异步调用
IAsyncResult result = MD.BeginInvoke(1, new AsyncCallback(CallbackFunction), "AsycState:OK", nul); // 启动调用,并继续向下执行,result监视异步操作,当出现result时,调用CallbackFunction函数,并将result传递给它
// ...
console.WriteLine("正在执行");
// ...
}
}