ChatGPT
C#语言通常被归类为一种通用的、面向对象的编程语言。它在许多领域都有广泛的应用,包括桌面应用程序、游戏开发、Web应用程序、移动应用程序等。具体而言:
-
桌面应用程序开发:C#主要用于开发Windows桌面应用程序。通过使用Windows Presentation Foundation (WPF)框架,您可以创建丰富而直观的桌面应用程序,具有各种交互式用户界面。
-
游戏开发:C#也在游戏开发中得到了广泛应用,特别是在使用Unity游戏引擎进行开发时。Unity支持C#脚本,开发者可以使用C#编写游戏逻辑和交互。
-
Web应用程序:虽然C#不像Java那样在大规模的后端开发中常见,但它也可以用于开发Web应用程序。通过ASP.NET框架,您可以使用C#开发Web应用程序和服务端代码。
-
移动应用程序:通过Xamarin框架,您可以使用C#开发跨平台的移动应用程序,包括iOS和Android应用。
-
人工智能:虽然C#并不是主要用于人工智能领域的语言,但它仍然可以用于实现一些人工智能相关的功能,特别是与.NET生态系统中的机器学习库结合使用。
-
其他领域:C#还可以用于开发工具、插件、脚本等各种应用。
总之,C#是一种多用途的编程语言,可以在许多不同的领域中发挥作用,尤其在Windows生态系统中有着强大的影响力。
Wikipedia
C#(发音为 See Sharp)是一种支持多种范例的通用高级编程语言。 C# 包含静态类型、强类型、词法范围、命令式、声明式、函数式、泛型、面向对象(基于类)和面向组件的编程学科。
C# 编程语言由 Microsoft 的 Anders Hejlsberg 于 2000 年设计,随后于 2002 年被 Ecma (ECMA-334) 批准为国际标准,并于 2003 年被 ISO/IEC (ISO/IEC 23270) 批准为国际标准。 Microsoft 与 .NET 一起引入了 C#。 Framework 和 Visual Studio,两者都是闭源的。当时,微软还没有开源产品。四年后,即 2004 年,一个名为 Mono 的免费开源项目启动,为 C# 编程语言提供跨平台编译器和运行时环境。十年后,微软发布了Visual Studio Code(代码编辑器)、Roslyn(编译器)和统一的.NET平台(软件框架),所有这些都支持C#,并且免费、开源、跨平台。 Mono 也加入了 Microsoft,但没有合并到 .NET 中。
截至 2022 年 11 月,该语言的最新稳定版本是 C# 11.0,它于 2022 年在 .NET 7.0 中发布。
设计目标
Ecma 标准列出了 C# 的以下设计目标:
- 该语言旨在成为一种简单、现代、通用、面向对象的编程语言。
- 该语言及其实现应该为软件工程原理提供支持,例如强类型检查、数组边界检查、尝试使用未初始化变量的检测以及自动垃圾收集。软件的稳健性、耐用性和程序员的生产力非常重要。
- 该语言旨在用于开发适合在分布式环境中部署的软件组件。
- 可移植性对于源代码和程序员来说非常重要,尤其是那些已经熟悉 C 和 C++ 的人。
- 对国际化的支持非常重要。
- C# 旨在适合为托管和嵌入式系统编写应用程序,范围从使用复杂操作系统的大型系统到具有专用功能的小型系统。
- 尽管 C# 应用程序的目的是在内存和处理能力要求方面更加经济,但该语言并不打算在性能和大小方面与 C 或汇编语言直接竞争。
Syntax 语法
C# 语言的核心语法与其他 C 风格语言(例如 C、C++ 和 Java)类似,特别是:
- 分号用于表示语句的结束。
- 大括号用于对语句进行分组。语句通常分为方法(函数),方法分为类,类分为命名空间。
- 变量使用等号分配,但使用两个连续的等号进行比较。
- 方括号与数组一起使用,既可以声明它们,也可以获取其中一个给定索引处的值。
Distinguishing features 显著特征
另请参阅:C# 和 Java 的比较
C# 区别于 C、C++ 和 Java 的一些显着特征包括:
Portability 可移植性
从设计上来说,C# 是最直接反映底层公共语言基础结构 (CLI) 的编程语言。它的大多数内在类型对应于CLI 框架实现的值类型。但是,语言规范没有规定编译器的代码生成要求:也就是说,它没有规定 C# 编译器必须以公共语言运行时为目标,或生成公共中间语言 (CIL),或生成任何其他特定格式。理论上,C# 编译器可以像传统的 C++ 或 Fortran 编译器一样生成机器代码。
Typing 打字
C# 支持使用关键字 var 进行强隐式类型变量声明,以及使用关键字 new[] 后跟一个隐式类型数组。集合初始值设定项。
C# 支持使用关键字 var 、进行强隐式类型变量声明,以及使用关键字 new[] 后跟一个隐式类型数组。集合初始值设定项。
C# 比 C++ 更类型安全。默认情况下,唯一的隐式转换是那些被认为是安全的,例如整数的扩展。这是在编译时、JIT 期间以及在某些情况下在运行时强制执行的。布尔值和整数之间以及枚举成员和整数之间不会发生隐式转换(文字 0 除外,它可以隐式转换为任何枚举类型)。任何用户定义的转换都必须显式标记为显式或隐式,这与 C++ 复制构造函数和转换运算符不同,默认情况下它们都是隐式的。
C# 明确支持泛型类型中的协变和逆变,与 C++ 不同,C++ 对逆变有一定程度的支持只需通过虚拟方法的返回类型的语义即可。
枚举成员放置在自己的范围内。
C# 语言不允许使用全局变量或函数。所有方法和成员都必须在类内声明。公共类的静态成员可以替代全局变量和函数。
与 C 和 C++ 不同,局部变量不能隐藏封闭块的变量。
Metaprogramming 元编程
元编程可以通过多种方式实现:
- 反射通过 .NET API 支持,从而支持类型元数据检查和动态方法调用等场景。
- 表达式树 将代码表示为抽象语法树,其中每个节点都是可以检查或执行的表达式。这使得可以在运行时动态修改可执行代码。表达式树为语言引入了一些同像性。
- 属性是可以附加到类型、成员或整个程序集的元数据,相当于 Java 中的注释。编译器和代码都可以通过反射访问属性。许多本机属性重复了 GCC 和 VisualC++ 的平台相关预处理器指令的功能。
- System.Reflection.Emit 命名空间,包含在运行时发出元数据和 CIL(类型、程序集等)的类。
- .NET 编译器平台 (Roslyn) 提供对语言编译服务的 API 访问,允许从 .NET 应用程序内编译 C# 代码。它公开了用于代码句法(词法)分析、语义分析、CIL 动态编译和代码发出的 API。
- 源生成器是 Roslyn C# 编译器的一项功能,可实现编译时元编程。在编译过程中,开发人员可以使用编译器的 API 检查正在编译的代码,并传递额外生成的 C# 源代码进行编译。
Methods and functions 方法和功能
C# 中的方法是类的成员,可以作为函数(指令序列)调用,而不仅仅是类属性的值保存功能。与其他语法相似的语言(例如 C++ 和 ANSI C)一样,方法的签名是一个声明,其中按顺序包含:任何可选的可访问性关键字(例如 private )、其返回类型的显式规范(例如 int ,或者关键字 void 如果没有返回值),方法的名称,最后是逗号分隔的参数规范的括号序列,每个参数规范由一个参数的类型、其正式名称以及可选的在未提供任何参数时使用的默认值。某些特定类型的方法,例如通过返回值或赋值简单地获取或设置类属性的方法,不需要完整的签名,但在一般情况下,类的定义包括其方法的完整签名声明。
与 C++ 类似,但与 Java 不同,C# 程序员必须使用作用域修饰符关键字 virtual 来允许子类重写方法。
C# 中的扩展方法允许程序员使用静态方法,就好像它们是类方法表中的方法一样,允许程序员向对象添加他们认为应该存在于该对象及其派生对象上的方法。
类型 dynamic 允许运行时方法绑定,允许类似 JavaScript 的方法调用和运行时对象组合。
C# 通过关键字 delegate 支持强类型函数指针。与 Qt 框架的伪 C++ 信号和槽一样,C# 具有专门围绕发布-订阅样式事件的语义,尽管 C# 使用委托来执行此操作。
C# 通过属性 [MethodImpl(MethodImplOptions.Synchronized)] 提供类似 Java 的 synchronized 方法调用,并通过关键字 lock 支持互斥锁。
Property 财产
C# 支持具有属性的类。这些属性可以是带有支持字段的简单访问器函数,也可以实现 getter 和 setter 函数。
自 C# 3.0 起,自动实现属性的语法糖可用, [76] ,其中访问器 (getter) 和修改器 (setter) 封装对类的单个属性的操作。
Namespace 命名空间
C# namespace 提供与 Java package 或 C++ namespace 相同级别的代码隔离,并且具有与 package 非常相似的规则和功能。 b3> .可以使用“using”语法导入命名空间。
Memory access 内存访问
在 C# 中,内存地址指针只能在专门标记为不安全 [78] 的块内使用,并且具有不安全代码的程序需要适当的权限才能运行。大多数对象访问是通过安全对象引用完成的,这些引用总是指向“活动”对象或具有明确定义的空值;不可能获得对“死”对象(已被垃圾收集的对象)或随机内存块的引用。不安全指针可以指向“非托管”值类型的实例,该值类型不包含对垃圾收集对象、数组、字符串或堆栈分配内存块的任何引用。未标记为不安全的代码仍然可以通过 System.IntPtr 类型存储和操作指针,但无法取消引用它们。
Exception 例外
程序员可以使用一系列标准异常。标准库中的方法在某些情况下会定期引发系统异常,并且通常会记录引发异常的范围。可以为类定义自定义异常类,从而允许根据需要针对特定情况进行处理。
C# 中不存在受检异常(与 Java 不同)。这是基于可扩展性和版本性问题的有意识的决定。
Polymorphism 多态性
与 C++ 不同,C# 不支持多重继承,尽管一个类可以实现任意数量的“接口”(完全抽象类)。这是该语言的首席架构师的设计决策,旨在避免复杂性并简化整个 CLI 的架构要求。
当实现包含同名方法的多个接口并以相同顺序(即相同签名)获取相同类型的参数时,与 Java 类似,C# 允许单个方法覆盖所有接口,并且如果需要,还允许覆盖特定方法每个接口。
但是,与 Java 不同,C# 支持运算符重载。
Language Integrated Query (LINQ) 语言集成查询 (LINQ)
C# 能够通过 .NET Framework 使用 LINQ。开发人员可以查询多种数据源,只要在对象上实现 IEnumerable<T>
接口。这包括 XML 文档、ADO.NET 数据集和 SQL 数据库。
在 C# 中使用 LINQ 带来的优势包括 Intellisense 支持、强大的过滤功能、具有编译错误检查功能的类型安全性以及通过各种源查询数据的一致性。C# 和 LINQ 可以使用多种不同的语言结构,它们是查询表达式、lambda 表达式、匿名类型、隐式类型变量、扩展方法和对象初始值设定项。
LINQ 有两种语法:查询语法和方法语法。但是,编译器始终在编译时将查询语法转换为方法语法。
using System.Linq;
var numbers = new int[] { 5, 10, 8, 3, 6, 12 };
// Query syntax
var numQuery1 =
from num in numbers
where num % 2 == 0
orderby num
select num;
// Method syntax
var numQuery2 =
numbers
.Where(num => num % 2 == 0)
.OrderBy(n => n);
Functional programming 函数式编程
虽然 C# 主要是一种命令式语言,但随着时间的推移,C# 总是会添加功能特性,例如:
- 充当一等公民 - C# 1.0 代表
- 高阶函数 - C# 1.0 与委托
- 匿名函数 - C# 2 匿名委托和 C# 3 lambda 表达式
- 闭包 - C# 2 与匿名委托以及 C# 3 与 lambda 表达式
- 类型推断 - 带有隐式类型局部变量 var 的 C# 3 和 C# 9 目标类型新表达式 new()
- 列表理解 - C# 3 LINQ
- 元组 - .NET Framework 4.0,但当 C# 7.0 引入具有语言支持的新元组类型时,它变得流行
- 嵌套函数 - C# 7.0
- 模式匹配 - C# 7.0
- 不变性 - C# 7.2 只读结构 C# 9 记录类型 和仅 Init 设置器
- 类型类 - C# 12 角色/扩展(开发中)
Common type system 通用类型系统
C#有统一的类型系统。这种统一的类型系统称为通用类型系统(CTS)。
统一的类型系统意味着所有类型(包括整数等基元)都是 System.Object 类的子类。例如,每个类型都继承一个 ToString() 方法。
Categories of data types 数据类型的类别
CTS 将数据类型分为两类:
- Reference types 参考类型
- Value types 值类型
值类型的实例既没有引用标识,也没有引用比较语义。值类型的相等和不等比较会比较实例内的实际数据值,除非相应的运算符重载。值类型派生自 System.ValueType ,始终具有默认值,并且始终可以创建和复制。值类型的其他一些限制是它们不能相互派生(但可以实现接口)并且不能具有显式默认(无参数)构造函数,因为它们已经具有一个隐式构造函数,该隐式构造函数将所有包含的数据初始化为依赖于类型的默认值( 0、null 或类似的值)。值类型的示例都是原始类型,例如 int (带符号的 32 位整数)、 float (32 位 IEEE 浮点数)、 char (以纳秒精度标识特定时间点)。其他示例有 enum (枚举)和 struct (用户定义的结构)。
相反,引用类型具有引用标识的概念,这意味着引用类型的每个实例本质上都与其他实例不同,即使两个实例中的数据相同。这反映在引用类型的默认相等和不等比较中,这些比较测试引用而不是结构相等,除非相应的运算符被重载(例如 System.String 的情况)。有些操作并不总是可行,例如创建引用类型的实例、复制现有实例或对两个现有实例执行值比较。尽管特定的引用类型可以通过公开公共构造函数或实现相应的接口(例如 ICloneable 或 IComparable )来提供此类服务。引用类型的示例有 object (所有其他 C# 类的最终基类)、 System.String (Unicode 字符字符串)和 System.Array (一个基类)。所有 C# 数组的类)。
这两种类型类别都可以使用用户定义的类型进行扩展。
Boxing and unboxing 装箱和拆箱
装箱是将值类型对象转换为相应引用类型的值的操作。C# 中的装箱是隐式的。
拆箱是将引用类型的值(之前已装箱)转换为值类型的值的操作。 C# 中的拆箱需要显式类型转换。 T 类型的装箱对象只能拆箱为 T(或可为 null 的 T)。
Example: 例子:
int foo = 42; // Value type.
object bar = foo; // foo is boxed to bar.
int foo2 = (int)bar; // Unboxed back to value type.
Examples
Hello World
以下是一个非常简单的 C# 程序,是经典“Hello world”示例的一个版本,使用 C# 9 中引入的顶级语句功能:
using System;
Console.WriteLine("Hello, world!");
对于用 C# 8 或更低版本编写的代码,程序的入口点逻辑必须编写在类型内的 Main 方法中:
using System;
// A version of the classic "Hello World" program
class Program
{
static void Main()
{
Console.WriteLine("Hello, world!");
}
}
此代码将在控制台窗口中显示以下文本:
Hello, world!
每行都有一个目的:
using System;
上面的行导入 System
命名空间中的所有类型。例如,稍后在源代码中使用的 Console
类是在 System
命名空间中定义的,这意味着无需提供类型的全名(包括命名空间)即可使用它。
// A version of the classic "Hello World" program
这一行是注释;它为程序员描述并记录了代码。
class Program
上面是 Program
类的类定义。一对大括号之间的所有内容都描述了该类。
{
...
}
大括号划分代码块的边界。在第一个实例中,它们标记 Program
类的开始和结束。
static void Main()
这声明了程序开始执行的类成员方法。 .NET 运行时调用 Main
方法。与 Java 不同, Main
方法不需要 public
关键字,该关键字告诉编译器该方法可以由任何类从任何地方调用。编写 static void Main(string[] args)
相当于编写 private static void Main(string[] args)
。 static 关键字使该方法无需 Program
实例即可访问。每个控制台应用程序的 Main
入口点必须声明为 static
,否则程序将需要 Program
的实例,但任何实例都需要程序。为了避免这种无法解决的循环依赖关系,如果没有 static Main
方法,处理控制台应用程序(如上所述)的 C# 编译器会报告错误。 void
关键字声明 Main
没有返回值。
Console.WriteLine("Hello, world!");
该行写入输出。 Console
是 System
命名空间中的静态类。它为控制台应用程序提供了标准输入、输出和错误流的接口。该程序调用 Console
方法 WriteLine
,该方法在控制台上显示一行带有参数的字符串 "Hello, world!"
。
GUI
Windows GUI 示例:
using System;
using System.Windows.Forms;
class Program
{
static void Main()
{
MessageBox.Show("Hello, World!");
Console.WriteLine("Is almost the same argument!");
}
}
此示例与前面的示例类似,不同之处在于它生成一个包含消息“Hello, World!”的对话框。而不是将其写入控制台。
Images
另一个有用的库是 System.Drawing
库,它用于以编程方式绘制图像。例如:
using System;
using System.Drawing;
public class Example
{
public static Image img;
static void Main()
{
img = Image.FromFile("Image.png");
}
}
这将创建一个与“Image.png”中存储的图像相同的图像。