简介

C# (英文名为 CSharp) 是微软开发的一种面向对象的编程语言,其语法与 C++ 类似,但在编程过程中要比 C++ 简单。C# 语言具备了面向对象语言的特征,即封装、继承、多态,并且添加了事件和委托,增强了编程的灵活性。

C# 与 .NET 的关系

.NET 是一个开发平台,而 C# 是一种在 .NET 开发平台上使用的编程语言,目前能在 .NET 平台上使用的开发语言很多,例如 Visual Basic .NET、Python、C#、Visual C++.NET 等。但在 .NET 平台上使用最多的是 C# 语言。
在这里插入图片描述

第一个C#程序

namespace FirstApp
{
    class Program
    {               
        static void Main(string[] args)
        {
        	 Console.WriteLine("FirstApp...");
		}
	}
}        

程序集

程序集(assemble)是编译好的,面向.NET Framework的代码逻辑单元。

私有程序集

私有程序集是最简单的一种程序集类型。私有程序集一般附带在某个软件上,且只能用于该软件。

共享程序集

共享程序集是其他程序可以使用的公共库。因为其他软件可以访问共享程序集,所以需要采取一定的保护措施来防止一下风险:

  • 名称冲突,另一个公司的共享程序集执行的类型与自己的共享程序集中的类型同名。
  • 程序集被同一个程序集的不同版本覆盖,新版本与某些已有的客户机带不兼容。

反射

因为程序集存储了元数据,包括在程序集中定义的所有类型和这些类型的成员的细节,所以可以
编程访问这些元数据。这个技术称为反射,该技术很有趣,因为它表示托管代码实际上可以检查其他托管代码,甚至检查它自己,以确定该代码的信息。它们常常用于获取特性的详细信息,也可以把反射用于其他目的,例如作为实例化类或调用方法的一种间接方式,如果把方法上的类名指定为字符串,就可以选择类来实例化方法,以便在运行时调用,而不是在编译时调用,例如根据用户的输入来调用(动态绑定)。

基本数据类型

C# 语言的数据类型分为值类型和引用类型。
值类型包括:整型、浮点型、字符型、布尔型、枚举型等;
引用类型包括:类、接口、数组、委托、字符串等。

整型

在这里插入图片描述

浮点型

在这里插入图片描述

decimal类型

在这里插入图片描述

bool类型

在这里插入图片描述

字符类型

在这里插入图片描述

预定义的引用类型

在这里插入图片描述

算术运算符

算术运算符是最常用的一类运算符,包括加法、减法、乘法、除法等。

运算符说明
+对两个操作数做加法运算,如:a+b
-对两个操作数做减法运算,如:a-b
*对两个操作数做乘法运算,如:a*b
/对两个操作数做除法运算,如:a/b
%对两个操作数做取余(取模)运算,如:a%b

逻辑运算符

逻辑运算符主要包括与、或、非等,它主要用于多个布尔型表达式之间的运算

运算符含义说明
&&逻辑与如果运算符两边都为 True,则整个表达式为 True,否则为 False;如果左边操作数为 False,则不对右边表达式进行计算,相当于“且”的含义
||逻辑或如果运算符两边有一个或两个为 True,整个表达式为 True,否则为 False;如果左边为 True,则不对右边表达式进行计算,相当于“或”的含义
!逻辑非表示和原来的逻辑相反的逻辑

比较运算符

比较运算符是在条件判断中经常使用的一类运算符,包括大于、小于、不等于、大于等于、小于等于等。

运算符说 明
==表示两边表达式运算的结果相等
!=表示两边表达式运算的结果不相等
>表示左边表达式的值大于右边表达式的值
<表示左边表达式的值小于右边表达式的值
>=表示左边表达式的值大于等于右边表达式的值
<=表示左边表达式的值小于等于右边表达式的值

位运算符

位运算包括与、或、 非、按位取反、按位异或、左移、右移等。

运算符说 明
&按位与。两个运算数都为 1,则整个表达式为 1,否则为 0;也可以对布尔型的值进行比较,相当于“与”运算,但不是短路运算
|按位或。两个运算数都为 0,则整个表达式为 0,否则为 1;也可以对布尔型的值进行比较,相当于“或”运算,但不是短路运算
~按位非。当被运算的值为 1 时,运算结果为 0;当被运算的值为 0 时,运算结果为 1。该操作符不能用于布尔型。对正整数取反,则在原来的数上加 1,然后取负数;对负整数取反,则在原来的数上加 1,然后取绝对值
^按位异或。只有运算的两位不同结果才为 1,否则为 0
<<左移。把运算符左边的操作数向左移动运算符右边指定的位数,右边因移动空出的部分 补 0
>>有符号右移。把运算符左边的操作数向右移动运算符右边指定的位数。如果是正值,左侧因移动空出的部分补 0;如果是负值,左侧因移动空出的部分补 1
>>>无符号右移。和 >> 的移动方式一样,只是不管正负,因移动空出的部分都补 0

三元运算符

布尔表达式 ? 表达式 1: 表达式 2

其中:

  • 布尔表达式:判断条件,它是一个结果为布尔型值的表达式。
  • 表达式 1:如果布尔表达式的值为 True,该三元运算符得到的结果就是表达式 1 的运算结果。
  • 表达式 2:如果布尔表达式的值为 False,该三元运算符得到的结果就是表达式 2 的运算结果。

赋值运算符

赋值运算符中最常见的是等号,除了等号以外还有很多赋值运算符,它们通常都是与其他运算符连用起到简化操作的作用。

运算符说 明
=x=y,等号右边的值给等号左边的变量,即把变量 y 的值赋给变量 x
+=x+=y,等同于 x=x+y
-=x-=y,等同于 x=x-y
*=x*=y,等同于 x=x*y
/=x/=y,等同于 x=x/y
%=x%=y,等同于 x=x%y,表示求 x 除以 y 的余数
++x++ 或 ++x,等同于 x=x+1
x-- 或 --x,等同于 x=x-1

需要注意的是,++ 和 – 运算符放在操作数前和操作数后是有区别的,如果放在操作数前,需要先将操作数加 1 或减 1,然后再与其他操作数进行运算;如果放在操作数后,需要先与其他操作数进行运算,然后操作数自身再加 1。

条件运算符

条件运算符(?:)也称为三元运算符,是 if…else 结构的简化形式。其名称的出处是它带有三个操作数。它可以计算一个条件,如果条件为真,就返回一个值;如果条件为假,则返回另一个值。其语法如下:condition ? true_value : false_value

checked 和 unchecked 运算符

C#提供了 checked 和 unchecked 运算符。如果把一个代码块标记为 checked,CLR 就会执行溢出检查,如果发生溢出,就抛出异常。下面修改代码,使之包含 checked 运算符:

byte b = 255;
checked
{
b++;
}
Console.WriteLine(b.ToString());
结果报错
byte b = 255;
unchecked
{
b++;
}
Console.WriteLine(b.ToString());
成功运行,输出为0

is运算符

is 运算符可以检查对象是否与特定的类型兼容。"兼容"表示对象是该类型,或者派生于该类型。
例如,要检查变量是否与 object 类型兼容,可以使用下面的代码:

int i=10;
if (i is object)
{
	Console.WriteLine("i is an object.");
}

as运算符

as 运算符用于执行引用类型的显式类型转换。如果要转换的类型与指定的类型兼容,转换就会成功进行;如果类型不兼容,as 运算符就会返回值 null。如下面的代码所示,如果 object 引用不指向 string实例,把 object 引用转换为 string 就会返回 null:

object obj1 = "Some String";
object obj2 = 5;
string s1 = obj1 as string; // s1="Some String"
string s2 = obj2 as string; // s2 = null

sizeof 运算符

使用 sizeof 运算符可以确定堆栈中值类型需要的长度(单位是字节):
sizeof(int);

typeof运算符

typeof 运算符返回一个表示特定类型的 System.Type 对象。
例如:typeof(string)返回表示System.String类型的Type对象。

可空类型和运算符

对于布尔类型,可以给它指定 true 或 false 值。但是,要把该类型的值定义为 undefined,该怎么
办?此时使用可空类型可以给应用程序提供一个独特的值。如果在程序中使用可空类型,就必须考虑null 值在与各种运算符一起使用时的影响。通常可空类型与一元或二元运算符一起使用时,如果其中一个操作数或两个操作数都是 null,其结果就是 null。例如:
int ? a = null;
int ? b = a+4; // b = null
int ? c = a*5; // c = null

空接合运算符

空接合运算符(??)提供了一种快捷方式,可以在处理可空类型和引用类型时表示 null 值。空接合运算符的计算如下:如果第一个操作数不是 null,则整个表达式就等于第一个操作数的值。但如果第一个操作数是 null,则整个表达式就等于第二个操作数的值。例如:
int? a = null;
int b;
b = a??10; // b = 10
a = 3;
b = a ?? 10; // b = 3

运算符的优先级

按优先级从高到低排序:

运算符结合性
.(点)、()(小括号)、[](中括号)从左到右
+ (正)、-(负)、++ (自增)、–(自减)、~(按位非)、!(逻辑非)从右到左
* (乘)、/ (除)、% (取余)从左向右
+ (加)、-(减)从左向右
<<、>>、>>>从左向右
<、<=、>、>=从左向右
==、!=从左向右
&从左向右
|从左向右
^从左向右
&&从左向右
||从左向右
?:从右到左
=、+=、-=、*=、/=、%=、&=、|=、^=、~=、<<=、>>=、>>>=从右到左

运算符重载

运算符重载的关键是在类实例上不能总是调用方法或属性,有时还需要做一些其他的工作,例如对数值进行相加、相乘或逻辑操作,如比较对象等。假定要定义一个类,表示一个数学矩阵,在数学中,矩阵可以相加和相乘,就像数字一样。所以可以编写下面的代码:
Matrix a,b,c;
Matrix d = c*(a+b)

如果用不支持运算符重载的语言编写代码,就必须定义一个方法,以执行这些操作,结果肯定不太直观,如下所示:
Matrix d = c.Multiply(a.Add(b))

下面是 Vector 的定义-- 包含成员字段、构造函数和一个 ToString()重写方法,以便查看 Vector 的
内容,最后是运算符重载:

namespace Wrox.ProCSharp.OOCSharp
{
	struct Vector
	{
		public double x, y, z;
		public Vector(double x, double y, double z)
		{
			this.x = x;
			this.y = y;
			this.z = z;
		}
		public Vector(Vector rhs)
		{
			x = rhs.x;
			y = rhs.y;
			z = rhs.z;
		}
		public override string ToString()
		{
			return "( " + x + " , " + y + " , " + z + " )"; 
		}
		public static Vector operator + (Vector lhs, Vector rhs)
		{
			Vector result = new Vector(lhs);
			result.x += rhs.x;
			result.y += rhs.y;
			result.z += rhs.z;
			return result;
		}
	}
}

运算符重载的声明方式与方法的声明方式相同,但 operator 关键字告诉编译器,它实际上是一个
运算符重载,后面是相关运算符的符号,在本例中就是+。

可以重载的运算符:
在这里插入图片描述

C#变量的定义

数据类型 变量名;
int num;

C#类型推断

类型推断使用 var 关键字。
var x=0;

C#变量的赋值

数据类型 变量名 = 值;

  • 先定义变量然后再赋值:
  • 数据类型 变量名;
  • 变量名 = 值;
    int a=1,b=2;
    a=5;
    b=12;

C#常量的定义

在声明和初始化变量时,在变量的前面加上关键字 const,就可以把该变量指定为一个常量:
const 数据类型 常量名 = 值;
const double PI = 3.1415926;

C#变量命名规则(命名规范)

常用的命名方法有两种,一种是 Pascal 命名法(帕斯卡命名法),另一种是 Camel 命名法(驼峰命名法)。

Pascal 命名法是指每个单词的首字母大写;Camel 命名法是指第一个单词小写,从第二个单词开始每个单词的首字母大写。

  1. 变量的命名规则
    变量的命名规则遵循 Camel 命名法,并尽量使用能描述变量作用的英文单词。例如存放学生姓名的变量可以定义成 name 或者 studentName 等。另外,变量名字也不建议过长, 最好是 1 个单词,最多不超过 3 个单词。
  2. 常量的命名规则
    为了与变量有所区分,通常将定义常量的单词的所有字母大写。例如定义求圆面积的 n 的值,可以将其定义成一个常量以保证在整个程序中使用的值是统一的,直接定义成 PI 即可。
  3. 类的命名规则
    类的命名规则遵循 Pascal 命名法,即每个单词的首字母大写。例如定义一个存放学生信息的类,可以定义成 Student。
  4. 接口的命名规则
    接口的命名规则也遵循 Pascal 命名法,但通常都是以 I 开头,并将其后面的每个单词的首字母大写。例如定义一个存放值比较操作的接口,可以将其命名为 ICompare。
  5. 方法的命名规则
    方法的命名遵循 Pascal 命名法,一般采用动词来命名。例如实现添加用户信息操作的方法,可以将其命名为 AddUser。

在 C# 语言中,除了上面涉及的内容外还有很多对象,但命名规则都是类似的,在涉及其他对象时还会对命名规则再次说明。

枚举

枚举是用户定义的整数类型。在声明一个枚举时,要指定该枚举可以包含的一组可接受的实例值。不仅如此,还可以给值指定易于记忆的名称。如果在代码的某个地方,要试图把一个不在可接受范围内的值赋予枚举的一个实例,编译器就会报告一个错误。
● 如上所述,枚举可以使代码更易于维护,有助于确保给变量指定合法的、期望的值。
● 枚举使代码更清晰,允许用描述性的名称表示整数值,而不是用含义模糊的数来表示。
● 枚举使代码更易于键入。在给枚举类型的实例赋值时,Visual Studio IDE 会通过 IntelliSense 弹
出一个包含可接受值的列表框,减少了按键次数,并能够让我们回忆起可选的值。

修饰符 enum 名称 <: 数据类型>

public enum Week : sbyte
{ 
     Sunday=0,
     Monday,
     Tuesday,
     Wednesday,
     Thursday,
     Friday,
     Saturday,
}

public enum Month
{ 
    January,
    February,
    Marquary,
    April,
    May,
    June,
    July,
    August,
    September,
    October,
    November,
    December,
}

数组

数据类型[] 数组名;
int[] integers;
integers = new int[32]

int[] integersX = new int[64];
int[] copy = integersX;

命名空间

命名空间提供了一种组织相关类和其他类型的方式。与文件或组件不同,命名空间是一种逻辑组合,而不是物理组合。在 C#文件中定义类时,可以把它包括在命名空间定义中。
namespace 命名
{

}

using 语句

显然,命名空间相当长,键入起来很繁琐,用这种方式指定某个类也是不必要的。如本章开头所
述,C#允许简写类的全名。为此,要在文件的顶部列出类的命名空间,前面加上 using 关键字。在文
件的其他地方,就可以使用其类型名称来引用命名空间中的类型了:
using System;
using Wrox.ProCSharp;

命名空间的别名

using 关键字的另一个用途是给类和命名空间指定别名。如果命名空间的名称非常长,又要在代
码中使用多次,但不希望该命名空间的名称包含在 using 指令中(例如,避免类名冲突),就可以给该
命名空间指定一个别名,其语法如下:
using alias = NamespaceName;

using System;
using Introduction = Wrox.ProCSharp.Basics;
class Test
{
	public static int Main()
	{
		Introduction::NamespaceExample NSEx =
		new Introduction::NamespaceExample();
		Console.WriteLine(NSEx.GetNamespace());
		return 0;
	}
}

Main()方法

C#程序是从方法 Main()开始执行的。这个方法必须是类或结构的静态方法,并且其返回类型必须是 int 或 void。虽然显式指定 public 修饰符是很常见的,因为按照定义,必须在程序外部调用该方法,但我们给该方法指定什么访问级别并不重要,即使把该方法标记为 private,它也可以运行。

控制台 I/O

● Console.ReadLine()方法从控制台窗口中读取一行文本。
● Console.ReadKey()读取Key。
● Console. Write()方法将指定的值写入控制台窗口。
● Console.WriteLine()方法类似,但在输出结果的最后添加一个换行符。

string s = Console.ReadLine();
Console.WriteLine(s);

int i = 10;
int j = 20;
Console.WriteLine("{0} plus {1} equals {2}", i, j, i+j);

在这里插入图片描述

decimal i = 940.23m;
decimal j = 73.7m;
Console.WriteLine(" {0,9:C2}\n+{1,9:C2}\n ------\n {2,9:C2}", i, j, i 
+ j);

C#预处理器指令

预处理器指令的开头都有符号#。
注意:
C++开发人员应知道在 C 和 C++中,预处理器指令是非常重要的,但是,在 C#中,并没有那么
多的预处理器指令,它们的使用也不太频繁。C#提供了其他机制来实现许多 C++指令的功能,例如定
制特性。还要注意,C#并没有一个像 C++那样的独立预处理器,所谓的预处理器指令实际上是由编译
器处理的。尽管如此,C#仍保留了一些预处理器指令,因为这些命令对预处理器有一定的影响。

#define 和 #undef
#define DEBUG
#undef DEBUG

#if, #elif, #else和#endif 用法和C/C++相同

#warning 和 #error
#error “Error Message.”
#warning “Warning Message.”

#region 和 #endregion
#region 和 #endregion 指令用于把一段代码标记为有给定名称的一个块.

#line
#line 指令可以用于改变编译器在警告和错误信息中显示的文件名和行号信息。这个指令用得并
不多。如果编写代码时,在把代码发送给编译器前,要使用某些软件包改变键入的代码,就可以使用
这个指令,因为这意味着编译器报告的行号或文件名与文件中的行号或编辑的文件名不匹配。#line
指令可以用于恢复这种匹配。

#pragma
#pragma 指令可以抑制或恢复指定的编译警告。与命令行选项不同,#pragma 指令可以在类或方
法上执行,对抑制警告的内容和抑制的时间进行更精细的控制。下面的例子禁止字段使用警告,然后
在编译 MyClass 类后恢复该警告。

使用注释

C#使用传统的 C 风格注释方式:

单行注释使用// …,多行注释使用/* … */:

// This is a single-line comment
/* This comment
spans multiple lines */

XML 文档说明

除了 C 风格的注释外,C#还有一个非常好的功能,本章将讨论这一功能。根据特定的注释自动创建 XML 格式的文档说明。这些注释都是单行注释,但都以 3 个斜杠(///)开头,而不是通常的两个斜杠。在这些注释中,可以把包含类型和类型成员的文档说明的 XML 标识符放在代码中。
在这里插入图片描述

// Math.cs
namespace Wrox.ProCSharp.Basics
{
	///<summary>
	/// Wrox.ProCSharp.Basics.Math class.
	/// Provides a method to add two integers.
	///</summary>
	public class Math
	{
		///<summary>
		/// The Add method allows us to add two integers
		///</summary>
		///<returns>Result of the addition (int)</returns>
		///<param name="x">First number to add</param>
		///<param name="y">Second number to add</param>
		public int Add(int x, int y)
		{
			return x + y;
		}
	}
}

C#关键字

在这里插入图片描述

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐