C#编程语言及.NET 平台快速入门指南

news2024/9/22 19:43:39
Office Word 不显示 Citavi 插件,如何修复?_citavi安装后word无加载项-CSDN博客  https://blog.csdn.net/Viviane_2022/article/details/128946061?spm=1001.2100.3001.7377&utm_medium=distribute.pc_feed_blog_category.none-task-blog-classify_tag-1-128946061-null-null.nonecase&depth_1-utm_source=distribute.pc_feed_blog_category.none-task-blog-classify_tag-1-128946061-null-null.nonecase

⼀、C#,CLR,IL,JIT概念 以及 .NET 家族

(⼀)基础概念

C# (念作 C Sharp) 是在CLR上实现的一种编程语言,也是.NET平台上最通用的编程语言,它在语法上借鉴了Java和C++风格,但更为精简。Borland Turbo Pascal编译器的主要作者安德斯·海尔斯伯格(Anders Hejlsberg)是C#与.NET平台的创始人。本文诣在为初次接触C#和.NET平台的用户提供较全面的路线指引,也为早期.NET开发人员介绍当代.NET平台的新特性。

相对于 C 和 C++,C# 在许多方面进行了限制和增强:

1、指针(Pointer)只能用于不安全模式之中。大多数对象访问通过安全的引用实现,以避免无效的调用,并且有许多算法用于验证溢出,指针只能用于调用值类型,以及受垃圾收集控制的托管对象。

2、对象不能被显式释放,代替为当不存在被引用时通过垃圾回收器回收。

3、只允许单一继承(single inheritance),但是一个类可以实现多个接口(interfaces)。

4、C# 比 C++ 更加类型安全。默认的安全转换是隐含转换,例如由短整型转换为长整型和从派生类转换为基类。而接口布尔型同整型,及枚举型同整型不允许隐含转换,非空指针(通过引用相似对象)同用户定义类型的隐含转换字段被显式的确定,不同于C++的复制构造函数。

5、数组声明语法不同("int[] a = new int[5]"而不是"int a[5]")。

6、枚举位于其所在的名字空间中。

7、C# 中没有模版(Template),但是在C# 2.0中引入了泛型(Generic programming),并且支持一些 C++ 模版不支持的特性。比如泛型参数中的类型约束。另一方面,表达式不能像C++模版中被用于类型参数。

8、属性支持,使用类似访问成员的方式调用。

9、完整的反射支持。

 CLR-Common Language Runtime 意为公共语⾔运⾏库,它是⼀个可由多种不同编程语⾔使⽤的运⾏库,只要是⾯向 CLR 的编译器编译的编程语⾔都被 CLR ⽀持。

 IL-Intermediate Language,意为中间语⾔,⾯向 CLR 的编程语⾔被编译为IL代码,IL代码也被称为托管代码,它是与 CPU ⽆关的机器语⾔,是⼀种⾯向对象的机器语⾔。每⼀个 IL 代码⽂件被称为托管模块(managed module)。托管模块是 ⼀个32位或是64位可移植执⾏体⽂件,它们需要CLR才能执⾏。

 每个托管模块带有相应的元数据(metadata),元数据描述模块中定义的内容,⽐如类型及成员、导⼊的类型及成员。每 个托管模块由操作系统头信息、CLR头(记录版本、⼊口⽅法等)、元数据、IL代码(CLR在运⾏时将IL编译成本地CPU 指令)。 ⼀个.NET程序集是由⼀个或者多个托管模块和资源⽂件组成,程序集是⼀个或是多个托管模块的逻辑分组,是最⼩的可重用、安全性及版本控制单元。

 JIT-just-in-time,意为CLR对IL代码进⾏即时编译的过程,CLR拥有进⾏JIT过程的编译器(JITComiler),它将要调⽤的 IL 代码编译为本地 CPU 指令。

.NET CORE 即将像C++一样支持 Intel CPU SIMD 指令集(从SSE到AVX2),参考下列资料:

Hardware intrinsic in .NET Core 3.0 - Introduction 英文

SIMD via C# 中文

(二).NET 家族

本文将Windows上的.NET Framework称为经典 .NET,由公共语⾔运⾏库(CLR)和类库(FCL --Framework Class Library)构成。

.NET Core 是 经典.NET 的跨平台实现,.NET Standard是 .NET Core 和 .NET Framework之间的通用库。

.NET 5 开始统一了.NET Core、.NET Framework 4.x、Mono等分支形成了一个统一的技术平台。

.NET 6 是首个原生支持苹果芯片 (Arm64) 的版本,并且还针对 Windows Arm64 进行了改进。C# 10和F# 6提供了语言改进,优化了代码,在性能上有了巨大的提升,使用dotnet monitor和OpenTelemetry改进了云诊断。ASP.NET Core 中引入了最少的 API,提高了 HTTP 服务的性能,.NET 6 开始引入了MAUI技术,提供跨系统平台的UI开发框架。

Mono是一个由Xamarin公司所主持的开源项目。该项目的目标是创建一系列匹配ECMA标准的.NET工具,包括C#编译器和通用语言架构。

ML.Net 是.NET上实现的AI开发框架(自.NET Core 就开始存在)。

开发Windows应用建议选择经典.NET (v.4.x)或 .NET Core 3.1 (Winfrom和WPF);
开发面向Window 10/11的桌面程序时,建议使用.NET 6 和 Windows App SDK + MAUI;

开发Linux上的微服务、Web服务、docker容器服务建议使用.NET Core (v.2.2.x、v.3.0.x、v.5.0.x、v.6.0.x) ;

开发跨平台手机应用建议使用.NET Xamarin框架(支持ios,Aandroid)或者 .NET 6之上的MAUI;

.NET Core 3.0 正式公布:新特性详细解读

.NET Core 3.0正式公布:新特性详细解读_文化 & 方法_Richard Lander_InfoQ精选文章


.NET 6.0 

.NET 生态系统的蜕变之 .NET 6 - 张善友 - 博客园 (cnblogs.com)


.NET 6 正式发布,迄今为止最快的.NET

Announcing .NET 6 - The Fastest .NET Yet - .NET Blog (microsoft.com)

注:Visual Studio 2019 支持用户使用以上任何一个框架开发应用,并内置相关应用场景的项目模板。

Visual Studio 2017支持.NET v.4.x 及.NET Core 2.2的项目开发。

微软公司在2014年开源了Roslyn编译器,随后成立了.NET 开源基金会,并在 Github上以MIT协议公开了.NET源代码。详情参考: .NET Platform · GitHub

.NET 5 在2020年推出,它将统一目前所有的 .NET 分支,.NET 6 已经于2021年11月8日正式发布。

上图为.NET 5 架构图

上图是 .NET 发布路线图

上图是.NET 6的架构

一家名为iolevel的公司推出了peachpie编译器(PeachPie.io - PeachPie | PHP compiler to .NET)致力于将PHP语言带到.NET平台。他们之前是一所大学团体(位于布拉格的查尔斯大学),推出过名为Phalanger的编译器( GitHub - DEVSENSE/Phalanger: PHP 5.4 compiler for .NET/Mono frameworks. Predecessor to the opensource PeachPie project (www.peachpie.io).)将一部分Facebook的开源代码转换为.NET代码来执行。这些都可以证明.NET平台的先进性。点击这里了解一些peachpie的背景:使用Phalanger整合PHP和.Net_知识库_博客园

二、C# 语言要点

(一)基元类型

C#类型FCL 类型说明
SbyteSystem.Sbyte有符号8位值 (⼀位即1bit,8bit即1byte,下同)
byteSystem.Byte⽆符号8位值
ShortSystem.Int16有符号16位值
ushortSystem.UInt16无符号16位值
intSystem.Int32有符号32位值
uintSystem.UInt32无符号32位值
LongSystem.Int64有符号64位值
ULongSystem.UInt64无符号64位值
charSystem.Char16位Unicode字符
FloatSystem.SingleIEEE32位浮点
DoubleSystem.DoubleIEEE64位浮点
BoolSystem.Boolean⼀个true/false值
DecimalSystem.Decimal128位⾼精度浮点值
StringSystem.String⼀个字符数组
ObjectSystem.Object所有类型的基类型

(二)引用类型和值类型

CLR⽀持引⽤类型和值类型。 引⽤类型总是从托管堆上分配,C#的new操作符会返回对象的内存地址。结构与枚举都是值类型,与引⽤类型相⽐,值类型是⼀种轻量级的类型,值类型实例是在线程的堆栈上分配,值类型不需要内存指针,不需要垃圾收集处理。所有类型 都是System.Object派⽣,所有值类型都是由System.ValueType抽象类派⽣。

(三) 值类型的装箱与拆箱:

当需要⼀个值类型进⾏实例引⽤时产⽣装箱(boxing) ,装箱过程是从托管堆中分配内存,并将值类型字段复制到新分 配的堆内存,然后返回新对象的引⽤。 

装箱情景:

复制代码

Struct Point{public int32 x,y;}
 
 Public sealed class Program
 { 
    Public static void Main()
     {
         ArrayList a = new ArrayList();
         Point p; 
         For(int32 i =0;i<10;i++) 
         {
             p.x = p.y = i;
             a.Add(p);//这⾥产⽣装箱,Add⽅法⼊参必须是Object类型,⽽Object类型是⼀个引⽤类型,值类型P要被装箱为引⽤ 类型。
         }         
     } 
}

复制代码

上例中,ArrayList内的p元素是引⽤类型,与原 Point P 结构脱离了关系。

 拆箱情景(unboxing): 

Point p2 =(Point)a[0];//这⾥产⽣拆箱

拆箱是获取已装箱对象各个字段的地址(拆箱关键),并将已经装箱的对象的字段值复制到新的值类型变量的字段。拆 箱时只能将对象拆箱为它装箱时的类型。

⼿动控制装箱的速度将⽐编译器装箱的速度快。 

如:

 1)Int32 v =5;Console.writeLine(“{0}{1}{2}”,v,v,v);

 2)Int32 v=5;object o =v(⼿动装箱);Console.writeLine(“{0}{1}{2}”,o,o,o)//这个⽅法快

(四)类型、类成员、接口

类型基础

类型:是可以在类型内部嵌套地定义其他类型的逻辑单位。

类型的成员种类:常量、字段、实例构造器、类型构造器(静态构造)、⽅法、操作符重载、转换操作符、属性、静态 事件、实例事件。

访问修饰符表:

名称用作类用作类成员

Public

该类型对所有程序集是可见的成员可以由所有程序集的所有⽅法访问
Protected internal成员可以由所在类型及其嵌套类型、所有派⽣类型 (不限程序集)、类型所在程序集的所有⽅法访 问。
Internal该类型仅在程序集内部可见以及友元程序 集可见成员可由当前程序集中的所有⽅法访问
Protected成员只能由定义该成员类型中的⽅法、该类型的所 有嵌套类型的⽅法、或者该类型的⼀个派⽣类型 (不限程序集)的⽅法访问
Private成员只能由定义该成员的类型中的⽅法或者该类型 的所有嵌套类型中的⽅法访问

组件版本控制修饰符表:

名称用作类用作方法、属性、事件⽤作常量/字段
Abstract表⽰该类型不能构建实例表⽰在构建派⽣类的实例之前派⽣类 型必须实现这个成员
Virtual表⽰这个成员可以由派⽣类型重写
Override表⽰派⽣类型重写了基类型的成员
Sealed表⽰该类型不能⽤作基类表⽰该成员不能被派⽣类型重写
new应⽤于嵌套类型、⽅法、属性、事件、常量或字段时,表⽰该成员与基类中类似的成员没有关系

静态类(static class):静态类是不需要实例化,仅拥有静态成员的类型。静态类不⽀持接⼜,这是因为只有使⽤类的实 例的时候才调⽤类的接⼜⽅法。静态类型只包括静态成员,静态类本⾝不能⽤作字段、⽅法参数或者局部变量。

部分类(partial class):为了将⼀个类分布在多个⽂件中编辑⽽采⽤partial修饰符,它们在编译后成为⼀个类。

索引器(indexer):索引器是⼀种参数化的成员属性。索引器不⽀持静态类型。索引器的作⽤是为类型向外界间接提供 内部的集合成员。

例:

public object this[int x]{get;set;},public object this[int x,int y]{get;set;}

可变参数⽅法:以params关键字修饰的参数称为可变参数,它允许输⼊数量不定的参数来调⽤⽅法。

例:

 Public static double GetAvg(params double[] list){…}; GetAvg(1,2,12,4,3.2);GetAvg(1,57.3);

基类初始化(initializer)调⽤:⼦类在实例化时可以⼀并调⽤基类的构造函数。这在多个类共享基类构造函数设置的⼀ 些公共成员属性时更便利。

例:

复制代码

Public class ClassA
{
    public ClassA(int a,string b){…}
}
 
Public class ClassB:ClassA
{
    public ClassB(int a,string b,bool c):base(a,b){…}
} 

复制代码

类型的私有构造函数常被⽤于只通过静态⽅法和字段来提供功能的类型。采⽤私有构造函数的类不能被外部类实例化, 但可以在内部实例化。

 静态构造函数⽤于初始化静态成员,也只能访问静态成员,不管类型被实例化多少次,静态构造函数只执⾏⼀次。

C# 特性标记的使用

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class, Inherited = true)]
public class CustomerAttribute:Attribute
{
      public String Name{get;set;}   
}

使用反射获取特性标记值

[CustomerAttribute(){Name="Sample"}]
public class Sample(){}
Sample o = new Sample();
Type ot = typeof(o);//typeof(t)
ot.GetCustomAttributes();

C# 匿名扩展方法

复制代码

public class A
{
      public A();
     public void M1();
}
 
public static class M
{
      public static M2(this A a )
      {
            //do sth.
      }
}

复制代码

(五) 集合类型 Array\ArrayList\List\HashTable(哈希表)\Dictionary(字典)\Stack(堆栈)\Queue(队列)

Array类型:是实现数组的基类,只有系统和编译器可以派⽣。Array提供CreateInstance⽅法进⾏后期绑定,没有公共构 造函数。以下都是声明数组的⽅式:

Array my1DArray=Array.CreateInstance( typeof(Int32), 5 ); 

Int32[] my2DArray = new Int32[5]{1,2,3,4,5}

ArrayList类型:是⼤⼩按需⾃动增加的Array实现,实现了IList接口。以下是ArrayList常见⽤法:

ArrayList myAL = new ArrayList(); 

myAL.Add("Hello");

myAL.Add("World"); myAL.Add("!");

HashTable: 表⽰键/值对的集合,这些键/值对根据键的哈希代码进⾏组织。

例:

复制代码

public static void Main() {
// Creates and initializes a new Hashtable.
Hashtable myHT = new Hashtable();
myHT.Add("First", "Hello");
myHT.Add("Second", "World");
myHT.Add("Third", "!");
// Displays the properties and values of the Hashtable.
Console.WriteLine( "myHT" );
Console.WriteLine( " Count: {0}", myHT.Count );
Console.WriteLine( " Keys and Values:" );
PrintKeysAndValues( myHT );
}
public static void PrintKeysAndValues( Hashtable myHT ) {
Console.WriteLine( "\t-KEY-\t-VALUE-" );
foreach ( DictionaryEntry de in myHT )
Console.WriteLine("\t{0}:\t{1}", de.Key, de.Value);
Console.WriteLine();
}

复制代码

Dictionary:是HashTable的泛型实现

Stack:表⽰对象的简单的后进先出⾮泛型集合。Stack 的容量是 Stack 可以保存的元素数。Stack 的默认初始容量为 10。 向 Stack 添加元素时,将通过重新分配来根据需要⾃动增⼤容量。Stack常被当作循环缓冲区。

Queue(队列):是表⽰对象的先进先出集合,与Stack相反。队列在按接收顺序存储消息⽅⾯⾮常有⽤,以便于进⾏顺 序处理。此类将队列作为循环数组实现。存储在 Queue 中的对象在⼀端插⼊,从另⼀端移除。

Queue 的容量是 Queue 可以保存的元素数。Queue 的默认初始容量为 32。向 Queue 添加元素时,将通过重新分配来根据 需要⾃动增⼤容量。

(六)泛型

泛型(generic)是CLR与编程语⾔提供的⼀种实现“算法重⽤”的机制。

例:

 List sl = new List();sl.add(DateTime.Now);sl.add(DateTime.MinValue);

泛型对象设计⽤于管理在类型上成家族的集合,例如设计⼀个⼯⼚类型⽤于创建或修改基于某个接口演变的多个⼦类型 的对象。

例:

复制代码

/// <summary>
/// 为安全成员对象提供公共服务
/// </summary>
public abstract class SecurityMemberService<T> where T:ISecurityMember
{
    public abstract T MemberLogin(string memberUserName,string memberPassword);
    public abstract T MemberLogin(string memberEmail,string memberPassword);
    public abstract bool MemberLogout(T member);
    public abstract T CreateMember(T obj,SecurityMemberInfo info);
    public abstract bool DeleteMember(T member);
    public abstract T FindMemberByUserName(string userName);
    public abstract T FindMemberByEmail(string email);
    public abstract bool LockMember(T member);
    public abstract bool UnlockMember(T member);
    public abstract bool ChangePassword(string memberName, string oldPassword,string newPassword);
    public abstract bool ChangePasswordQuestionAndAnswer(T member);
    public abstract bool ResetPasswordAndUpdate(T member);
}

复制代码

在上例中,SecurityMemberService类型封装了⼀般对ISecurityMember类型的处理⽅法,类型参数T可以是任意 实现了ISecurityMember接⼝的类型,这样对这些类型的⼀般处理并不需要创建额外对应的⼯⼚类型。 注意:泛型类SecurityMemberService有⼀个对类型参数T的约束,它由where关键字指定。

在⾮泛型类中也可以有泛型⽅法成员,同样泛型⽅法也可有类型约束。

例:

Public class A

{

Void M1<T>(T obj){obj.ToString();}

Void M2<T>(T obj)where T:ClassB {obj.ToString();}

}

委托也可以被设计成泛型,因为委托也可以被当作⽅法的⼀种定义形式,即委托本身描述的是回调⽅法的定义。

例:

Delegate void EventHandler(Object sender,TEventArgs e)where TEventArgs:EventArgs;

上例定义的EventHandler要求回调⽅法中的参数e必须是EventArgs类型或是EventArgs的派⽣类型,TEventArgs 是⼀个类型参数,相当于常⻅的T。

(七)线程 (Threading、Lock、Monitor、Mutex)

线程概述:

线程分为前台线程和后台线程,后台线程不妨碍程序的终⽌。线程具有优先级,优先级⾼的线程会得到更多的CPU时 间。多线程可以提⾼对CPU时间的利⽤率,但会占⽤更多的内存等资源。

线程安全:

Lock关键字可以将⼀段代码定义为互斥段。互斥段在⼀个时刻内只允许⼀个线程进⼊执⾏,⽽其他线程必须等待。如果 有⼀些任务每次只能交给⼀个线程去操作,就可以使⽤Lock关键字将代码定义为互斥段。

例:

Lock(this) 

{

     //do anything 

}

Monitor 类通过向单个线程授予对象锁来控制对对象的访问。对象锁提供限制访问代码块(通常称为临界区)的能⼒。当 ⼀个线程拥有对象的锁时,其他任何线程都不能获取该锁。还可以使⽤ Monitor 来确保不会允许其他任何线程访问正在由 锁的所有者执⾏的应⽤程序代码节,除⾮另⼀个线程正在使⽤其他的锁定对象执⾏该代码。

例:

Queue myQueue = new Queue();

Monitor.Enter(myQueue);

//可以在当前线程下对myQueue做任何操作。

Monitor.Exit(myQueue)//释放锁

为了保证在异常情况下仍可释放锁,Monitor.Exit()⽅法可以放在finally块⾥。调⽤Monitor.Pulse()⽅法会通知预备队列中的 线程可以⽴即使⽤释放的对象。

Mutex类是同步基元。当两个或更多线程需要同时访问⼀个共享资源时,系统需要使⽤同步机制来确保⼀次只有⼀个线程 使⽤该资源。

Mutex只向⼀个线程授予对共享资源的独占访问权。如果⼀个线程获取了互斥体,则要获取该互斥体的第⼆个线程将被挂 起,直到第⼀个线程释放该互斥体。已命名的系统互斥体(Mutex)在整个操作系统中都可见,可⽤于同步进程活动。

与Monitor类不同,Mutex可与WaitHandle⼀起构成“等待机制”,Mutex还可以穿越应⽤程序域。

例:

复制代码

class Test
{
    // Create a new Mutex. The creating thread does not own the
    // Mutex.
    private static Mutex mut = new Mutex();
    private const int numIterations = 1;
    private const int numThreads = 3;
    static void Main()
    {
        // Create the threads that will use the protected resource.
        for(int i = 0; i < numThreads; i++)
        {
            Thread myThread = new Thread(new ThreadStart(MyThreadProc));
            myThread.Name = String.Format("Thread{0}", i + 1);
            myThread.Start();
        }
        // The main thread exits, but the application continues to
        // run until all foreground threads have exited.
    }
    private static void MyThreadProc()
    {
        for(int i = 0; i < numIterations; i++)
        {
            UseResource();
        }
    }
    // This method represents a resource that must be synchronized
    // so that only one thread at a time can enter.
    private static void UseResource()
    {
        // Wait until it is safe to enter.
        mut.WaitOne();
        Console.WriteLine("{0} has entered the protected area",
        Thread.CurrentThread.Name);
        // Place code to access non-reentrant resources here.
        // Simulate some work.
        Thread.Sleep(500);
        Console.WriteLine("{0} is leaving the protected area\r\n",
        Thread.CurrentThread.Name);
 
        // Release the Mutex.
        mut.ReleaseMutex();
    }
}

复制代码

(八) C# 面向对象编程、继承、多态、接口、委托、事件

基本概念

⾯向对象编程(Object –Oriented Programming,OOP),抽象、继承和多态是OOP编程语⾔的三⼤要素。

继承:类继承的重要特性是,在希望出现基类型实例的任何地⽅,都可以替换成派⽣类型的实例。类似地,接口继承允许在希望出现已命名接口类型的实例的任何地⽅,都可以替换成实现接口的⼀个类型的实现。

多态:指的是多个类型的对象对同⼀消息做出各⾃的处理。多态是⼦类对⽗类的⽅法进⾏重写或替换⽽实现的。 

接口:接口是⼀组已命名的⽅法签名,在接口内还可以定义事件和属性,它们在本质上也是⽅法。C# 要求接口⽅法标记为 Public。接口的关键价值在于隐藏类型的设计细节,即外部对象不依赖当前对象的内部细节。

接口特性

接口⽅法的隐式实现:当⽅法签名与继承的接口中的签名⼀致,并且是public或者是viture修饰的⽅法都视为隐式实现了接口⽅法。

例:

Internal sealed class SimpleType:IDisposable

{

Public void Dispose(){Console.WriteLine(“Dispose”);}

}

接口⽅法的显式实现:以接口类型名称作为⽅法前缀时,创建的是⼀个显式接口⽅法实现(explicit interface method implementation,EIMI)。⼀个EIMI⽅法不允许标记访问性(⽐如公共或私有),也不能被标记为virture,因⽽也不能被重 写。显⽰接口⽅法会损害性能,应当谨慎使⽤。

例:

Internal sealed class SimpleType:IDisposable

{

Public void Dispose(){….}

Void IDisposable.Dispose(){….}//显式

}

对显式接口的调⽤,需要通过⼀个接口类型的变量来进⾏。

例:

SimpleType st = new SimpleType();

IDisposable d = st;

d.Dispose();

泛型接口有如下优点:

1、使用接口方法变为强类型。

2、泛型接口在操作值类型时,会减少装箱操作。

3、类可以实现同一个接口若干次,只要使用不同的类型参数。

例:

复制代码

Public sealed class Number:IComparable<Int32>,IComparable<String>
{
Private int32 m_val =5;
 
//实现IComparable<Int32>
Public Int32 CompareTo(Int32 n){return m_val.CompareTo(n);}
 
//实现IComparable<String>
Public Int32 CompareTo(String s){return m_val.CompareTo(Int32.Parse(s));}
} 

复制代码

委托

委托是.NET中的回调机制。将一个方法绑定到一个委托时,C#和CLR允许引用类型的协变(covariance)和反协变(contra-variance)。协变是指一个方法能返回一个从委托的返回类型派生出来的类型。反协变是指一个方法的参数类型可以是委托的参数类型的基类。但协变对于返回值类型或void的方法不适用。

例:

//MyCallback委托

Delegate object MyCallback(FileStream s);

 //SomeMethod⽅法 

String SomeMethod(Stream s);

上例中,SomeMethod的返回类型(String)继承⾃委托返回类型(Object),这种协变是允许的。SomeMethod的参数类型

(Stream)是委托的参数类型(FileStream)的基类。这种反协变是允许的。

链式委托指的是⽤⼀个委托回调多个⽅法,即⼀系列委托对象组成的集合。Delegate的公共静态⽅法Combine⽤于添加⼀ 个委托到委托链,Remove⽅法⽤于从链中删除⼀个委托对象。在C#中内置的+=与-=操作符简化了这些操作。

例:

Internal delegate void Feedback(int32 value);
Feedback fb1 = new Feedback(….);
Feedback fb2 = new Feedback(….);
fbChain =(Feedback)Delegate.Combine(fbChain,fb1);
fbChain =(Feedback)Delegate.Combine(fbChain,fb2); 

⼀组委托是按顺序执⾏的,如果他们带有返回值,只能得到最后⼀个委托的返回值,如果其间有委托⽅法出现致命错误,其它委托就⽆法执⾏。为了克服这些问题,产⽣了MulticastDelegate类,它的GetInvocationList⽅法⽤于显式调⽤链中的每 ⼀个委托,并使⽤符合⾃⼰需求的任何算法。MulticastDelegate类是特殊的类型,只能由系统派⽣,Delegate类已经具备了 MulticastDelegate的能⼒。

委托的便捷实现:

1. 不构造委托对象

例:

复制代码

internal sealed class AClass 
{ 
     public static void CallbackWithoutNewingADelegateObject()  
    { 
        ThreadPool.QueueUserWorkItem(SomeAsyncTask,5); 
    } 
   
   private static void SomeAsyncTask(Object o)
   {
       Console.WriteLine(o);
   } 
}

复制代码

上例中ThreadPool类的静态⽅法QueueUserWorkItem期望接收⼀个WaitCallback委托对象引⽤,该对象又包含⼀个 SomeAsyncTask⽅法引⽤。因为C#编译器能够⾃⼰进⾏推断,所以我们可以省略构造WaitCallback对象的代码。

2. 不定义回调⽅法

例: 

复制代码

internal sealed class AClass 
{ 
     public static void CallbackWithoutNewingADelegateObject() 
     {  
        ThreadPool.QueueUserWorkItem(delegate(Object obj){Console.WriteLine(obj);},5)  
     } 
} 

复制代码

上例中⽤了⼀段代码块替代了回调⽅法名,编译器会⾃动在类中增加⼀个经过命名的基于此代码块的回调⽅法。

3. 不指定回调⽅法的参数

例:

button1.Click += delegate(Object sender,EventArgs e){MessageBox.Show(“The Button was clicked”);} 

//由于上述⽅法中没有⽤到sender与e两个参数,可简写为:

button1.Click+=delegate{MessageBox.Show(“ The Button was clicked”);}

4. 不需要将局部变量⼈⼯封装到类中,即可传给⼀个回调⽅法

事件

事件:在.NET中事件(event)是类的成员,与成员属性和⽅法⼀样。类型的事件,是对外提供的⾃⾝状态的通知。外部类 型通过订阅的形式与事件的发布类型进⾏协作。将事件与处理⽅法关联起来的是委托。.NET中⽤event关键指定特定的委托 来为事件做出响应,这样做可以限制其它⽅法对委托的调⽤(在内部定义委托为私有的,通过event公开,因此外部⽆法访 问委托中的⽅法)。

设计线程安全的事件,必须显⽰地控制事件的订阅与注销。

例:

复制代码

internal class MailManager
{
    //创建⼀个作为线程同步锁的私有实例字段
    private readonly Object m_eventLock = new Object(); 
 
    //增加⼀个引⽤ 委托链表头部的私有字段
    private EventHadler<NewMailEventArgs> m_NewMail;
 
    //增加⼀个事件成员
    public event EventHandler<NewMailEventArgs> NewMail
    {
        //显式实现add
        add
        {
         //加私有锁,并向委托链表增加⼀个处理程序以‘value’为参数
        lock(m_eventLock){m_NewMail+=value;}  
       }
      //显式实现remove
     remove 
     {
      //加私有锁,并向委托链表移除⼀个处理程序以‘value’为参数 
      lock(m_eventLock){m_NewMail -= value;}
      } 
    }
 
    //定义⼀个负责引发事件的⽅法,来通知已订阅事件的对象事件已经发⽣,如果类是封装的
    //则需要将⽅法声明为private和non-virtual
    proteted virtual void OnNewMail(NewMailEventArgs e) 
    { 
      //出于线程安全考虑,将委托字段保存到⼀个临时字段中
      EventHadler<NewMailEventArgs> temp = m_NewMail; 
      if(temp!=null){temp(this,e);} 
     } 
 
    //将输⼊转化为希望的事件
    public void SimulateNewMail(String from,String to,String subject) 
     {
         //构建⼀个对象存放给事件接收者的信息
        NewMailEventArgs e = new NewMailEventArgs(from,to,subject);  
        //引发 
        OnNewMail(e);  
      }
}

复制代码

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

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

相关文章

2024下半年小红书短剧营销怎么玩?

短剧赛道不断升温&#xff0c;各行业品牌相继入局&#xff0c;「韩束」借短剧推爆新品&#xff0c;实现社媒平台的流量断层第一&#xff1b;「茶百道」在剧中植入产品和门店位置&#xff0c;品牌曝光量过亿…… 同时&#xff0c;小红书在6月上海微短剧大会上宣布大力拓展微短剧…

基于Tomcat的JavaWeb(ASP)项目构建(图解)

目录 配置IDEA的TOMCAT环境 环境设置 导入API(可选) 创建项目 构建项目 ​编辑 运行项目 项目结果 ​编辑 查看配置基础项目 配置IDEA的TOMCAT环境 环境设置 导入API(可选) 创建项目 构建项目 运行项目 项目结果 查看配置基础项目 了解Web Application: Exploded与…

值得入手的蓝牙耳机品牌推荐?四款值得入手的开放式耳机推荐

如果说推荐入手什么蓝牙耳机的话&#xff0c;我会推荐入手开放式耳机&#xff0c;因为在蓝牙耳机里&#xff0c;开放式的耳机类型其实也很有特点。它拥有更宽广的音场&#xff0c;因为其开放式的耳机设计&#xff0c;所以声音传播更接近自然环境&#xff0c;听音乐也能享受到身…

eclipse 导入或者打开项目总是弹出Marketplace Solutions available

如图所示&#xff1a; 解决方案&#xff1a; 打开项目的.projec文件夹&#xff0c;注释掉org.zeroturnaround.eclipse.jrebelNature即可

腾讯云 Nginx 服务器 SSL 证书安装部署(Linux)

看起来大家庭关注这个&#xff0c;刚发了一个《Nginx 服务器 SSL 证书安装部署&#xff08;Windows&#xff09;》&#xff0c;点击量还挺高&#xff0c;那就再发一个linux下的过程吧。希望能对大家有所帮助。 照例&#xff0c;先放官方废话&#xff1a; 说明 1、本文档以证…

如何量化员工的幸福感?XSENS动捕技术为零售业员工健康提供保障

零售业员工在搬运与摆放货物时经常面临关节负载过大所导致的肌肉骨骼类疾病的困扰。通过使用Xsens动作捕捉技术COLRUYT集团成功解决了工人在日常工作过程中所面临的人体工程学挑战。这彻底改变了零售业的职业健康和安全问题&#xff0c;并进一步提高了员工在工作中的幸福感。 挑…

深入理解 Babel - 微内核架构与 ECMAScript 标准化|得物技术

随着浏览器版本的持续更新&#xff0c;浏览器对JavaScript的支持越来越强大&#xff0c;Babel的重要性显得较低了。但Babel的设计思路、背后依赖的ECMAScript标准化思想仍然值得借鉴。 本文涉及的Babel版本主要是V7.16及以下&#xff0c;截至发文时&#xff0c;Babel最新发布的…

[数据集][目标检测]轮胎检测数据集VOC+YOLO格式4629张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;4629 标注数量(xml文件个数)&#xff1a;4629 标注数量(txt文件个数)&#xff1a;4629 标注…

CDO的核心价值与角色深化

随着数字化浪潮席卷各行各业&#xff0c;首席数据官&#xff08;CDO&#xff09;这一角色日益显得重要&#xff0c;成为企业战略规划的核心。 他们的主要任务是深入挖掘数据潜能&#xff0c;通过精确的数据洞察为企业的成长和运营优化提供坚实的数据支持和策略指导。 首席数据…

C语言操作符详解2(含结构体、整型提升、算术转换)

文章目录 一、逗号表达式二、下标访问操作符[]、函数调用操作符()1.下标访问操作符[]2.函数调用操作符 三、结构成员访问操作符1.结构体2.结构的声明3.结构体变量的定义和初始化&#xff08;1&#xff09;结构体变量的定义&#xff08;2&#xff09;结构体变量的初始化 4.结构成…

数据结构(三)——双向链表,循环链表,内核链表,栈和队列

双链表 产生原因&#xff1a;单链表只有一个指向后继的指针&#xff0c;如果要访问某节点的前驱结点&#xff0c;只能从头遍历&#xff0c;也就是访问后继节点的时间复杂度为1&#xff0c;访问前驱结点的时间复杂度为n。 而引入双链表使得在插入、删除的…

C语言代码练习

今日练习&#xff1a; 34、通过指向结构体变量的指针变量变量输出结构体变量中的信息。 35、建立一个简单链表&#xff0c;它由3个学生数据的结点组成&#xff0c;要求输出各结点中的数据。36、从键盘输入一些字符&#xff0c;逐个把他们送到磁盘上去&#xff0c;直到用户输入一…

【电机控制】永磁同步电机FOC控制—电流环PI参数整定

0. 前言 在了解电流环PI参数整定之前我们需要有以下几点基础知识&#xff1a; 1、了解什么是传递函数、传递函数的零点和极点。 2、了解极点对系统稳定性的影响。 有基础的小伙伴可以跳着看。 1. 传递函数与零点极点 1.1 传递函数 系统传递函数G(s)的定义是&#xff1a;在指零…

Pytorch – YOLOv9自定义资料训练

本篇将讲解目前最新推出的YOLOv9搭配Roboflow进行自定义资料标注训练流程&#xff0c;透过Colab上进行实作说明&#xff0c;使大家能够容易的了解YOLOv9的使用。 ►YOLO框架下载与导入 ►Roboflow的资料收集与标注 进入Roboflow官网&#xff0c;点选右上Sign up注册自己的帐号…

网站设计公司设计费用

网站设计公司的设计费用往往是一个让人头疼的问题。设计费用的高低涉及到多个因素&#xff0c;包括公司规模、项目复杂性、设计师经验等。本文将深入探讨网站设计公司的设计费用&#xff0c;并提供一些建议&#xff0c;帮助您在有限预算下实现最佳性价比。 1. 项目规模与复杂性…

GitHub 上 Stars 数量最多的 8 个开源 CRUD 项目

继续我们的 GitHub Star 系列&#xff01;这是本系列的第四篇文章&#xff0c;之前的内容&#xff1a; GitHub Star 数量前 12 的开源无代码工具GitHub Star 数量前 15 的开源低代码项目GitHub Star 数量前 11 的开源内部工具 本期我们来盘点 CRUD 项目。在软件开发中&#x…

爬虫练习(猫眼电影解密)

问题 随便拿一篇电影做样例。我们发现猫眼的页面数据在预览窗口中全是小方框。在当我们拿到源码以后&#xff0c;数据全是加密后的。所以我们需要想办法破解加密&#xff0c;拿到数据。 破解过程 1.源码获取问题与破解 分析 在我们刚刚请求url的时候是可以得到数据的&#xff…

[Linux] 操作系统 入门详解

标题&#xff1a;[Linux] 操作系统 水墨不写bug 目录 一、冯 . 诺依曼体系结构 1.冯诺依曼体系结构简介 2.对冯诺依曼体系结构的理解 二、操作系统定位 1.为什么需要操作系统&#xff1f; 2.操作系统是什么&#xff1f; 三、系统调用和库函数 正文开始&#xff1a; …

可交互、会学习、自成长机器人——李德毅院士

在以“农业无人农场”为主题的中国工程科技论坛上&#xff0c;中国工程院院士、欧亚科学院院士、中国人工智能学会和中国指挥与控制学会名誉理事长&#xff0c;中科原动力首席科学家李德毅院士应邀做题为《机器具身交互智能》的演讲。李德毅院士表示&#xff0c;智能机器不但把…

CentOS7单机环境安装k8s集群

目录 1、环境准备 2、安装依赖工具 3、配置 Kubernetes 的国内 Yum 源 4. 安装 Kubernetes 组件 5、初始化 Kubernetes 集群 1. 容器运行时没有正常运行 1.1. 可能的原因 1.2. 解决办法 2. 初始化拉取镜像卡住 2.1. 使用国内的镜像源&#xff08;无法解决问题&#x…