【笔记】C#(高级)

本文介绍了C#中的特性如Conditional、Obsolete和自定义AttributeUsage,反射用于运行时获取类信息,属性和索引器作为对象成员访问方式,以及委托和事件的概念。还讨论了不同类型的异步编程模式,包括基于任务的异步模式(TAP)和基于事件的异步模式(EAP)。
摘要由CSDN通过智能技术生成

特性与反射

特性

.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)

基于任务的异步编程核心是TaskTask<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("正在执行");
        // ...
    }
    
}

基于事件的异步模式(EAP):不建议使用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值