这里注意win10虚拟机需要更新下补丁,不然直接下载visual studio 2022
会显示版本不支持
C#的类文件都是以.cs
结尾,入口方法为static void Main(string[] args)
using System; //using 类似于import 导入的意思,这里的System是一个命名空间类似于Java中的包,也就是导入第三方依赖
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
// 这里的Ant就是一个命名空间。我们自己写的一段代码
// 命名空间 对比java的包
namespace Ant
{
// 类
internal class Program
{
// 入口方法
static void Main(string[] args)
{
// 类似于sout 打印输出
Console.WriteLine("Hello World");
Console.Read(); //暂停,否则cmd窗口会一闪而过
}
}
}
命名空间
命名空间的设计目的是提供一种让一组名称与其他名称分隔开的方式。在一个命名空间中声明的类的名称与另一个命名空间中声明的相同的类的名称不冲突。
关键字是namespace
可理解为java下的package
例如下面这样写,程序依然可以照常运行
using System;
namespace first_space
{
class namespace_cl
{
public void func()
{
Console.WriteLine("Inside first_space");
}
}
}
namespace second_space
{
class namespace_cl
{
public void func()
{
Console.WriteLine("Inside second_space");
}
}
}
class TestClass
{
static void Main(string[] args)
{
first_space.namespace_cl fc = new first_space.namespace_cl();
second_space.namespace_cl sc = new second_space.namespace_cl();
fc.func();
sc.func();
Console.ReadKey();
}
}
命名空间可以被嵌套,即您可以在一个命名空间内定义另一个命名空间
namespace namespace_name1
{
// 代码声明
namespace namespace_name2
{
// 代码声明
}
}
using关键字
0x01 引入namespace
using 关键字表明程序使用的是给定命名空间中的名称。例如,我们在程序中使用 System 命名空间,其中定义了类 Console。我们可以只写:
Console.WriteLine ("Hello there");
0x02 using static 指令
指定无需指定类型名称即可访问其静态成员的类型
using static System.Math;var = PI; // 直接使用System.Math.PI
0x03 起别名
using Project = PC.MyCompany.Project;
0x04 using语句,将实例与代码绑定
这里其实有一个特性就是在 using 语句块结束的时候会调用对象的 IDispose.Dispose() 方法。一般我们会在非托管类型的 Dispose() 方法内部进行资源的释放。
using (Font font3 = new Font("Arial", 10.0f),
font4 = new Font("Arial", 10.0f))
{
// Use font3 and font4.
}
代码段结束时,自动调用font3和font4的Dispose方法,释放实例。
注释
//单行注释
/**/多行注释
///文档注释 便捷的给方法/类加注释,方法功能,方法入参等,鼠标悬停在对应方法名或class上会有注释的提示
分类为以下三种:
- 值类型(Value types)
- 引用类型(Reference types)
- 指针类型(Pointer types)
可以理解为基本数据类型。值类型变量可以直接分配给一个值。它们是从类 System.ValueType 中派生的。当您声明一个值类型的变量时,系统分配内存来存储值。
引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。
换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string。
- 对象(Object)类型:object是所有类的基类,Object)类型可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。当一个值类型转换为对象类型时,则被称为 装箱;另一方面,当一个对象类型转换为值类型时,则被称为 拆箱。
object obj;
obj = 100; // 这是装箱
- 动态(Dynamic)类型:可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。声明格式
dynamic <variable_name> = value;
- 字符串(String)类型:字符串(String)类型的值可以通过两种形式进行分配:引号和 @引号。C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待,比如:
string str = @"C:\Windows";
=>string str = "C:\\Windows";
指针类型变量存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。
声明格式:type* identifier;
例如:char* cptr;
?
单问号用于对 int、double、bool 等无法直接赋值为 null 的数据类型进行 null 的赋值,意思是这个数据类型是 Nullable 类型的。
int? num1 = null;
int? num2 = 45;
double? num3 = new double?();
double? num4 = 3.14157;
bool? boolval = new bool?();
// 显示值
Console.WriteLine("显示可空类型的值: {0}, {1}, {2}, {3}",
num1, num2, num3, num4);
输出:
显示可空类型的值: , 45, , 3.14157
一个可空的布尔值:
??
双问号用于判断一个变量在为 null 的时候返回一个指定的值。
double? num1 = null;
double? num2 = 3.14157;
double num3;
num3 = num1 ?? 5.34; // num1 如果为空值则返回 5.34
类型转换
变量的数据类型基本就是之前说过的数据类型,然后C#也是有enum
的, enum,也允许定义引用类型变量,比如 class。
变量的定义与赋值与java没什么区别
int d = 3, f = 5; /* 初始化 d 和 f. */
byte z = 22; /* 初始化 z. */
double pi = 3.14159; /* 声明 pi 的近似值 */
char x = 'x'; /* 变量 x 的值为 'x' */
常量的话注意声明时需要用const
const <data_type> <constant_name> = value; //声明格式
public const int c1 = 5;
public const int c2 = c1 + 5;
其他字符常量
大部分都差不多,主要看一下之前没遇到过的
x.m
成员访问
x(...)
方法和委托调用
x[...]
数组和索引器访问
newT(...)
对象和委托创建
newT(...){...}
使用初始值设定项创建对象
new{...}
匿名对象初始值设定项
newT[...]
数组创建
一元
+x
恒等
-x
求相反数
!x
逻辑求反
~x
按位求反
++x
前增量
--x
前减量
x++
后增量
x--
后减量
(T)x
将x显示转换为类型T
二元
x * y
乘法
x / y
除法
x % y
取余
x + y
加法,字符串串联
x - y
减法
x << y
位左移
x >> y
位右移
x < y
小于
x > y
大于
x <= y
小于或等于
x >= y
大于或等于
x is T
如果 x 位 T ,返回true
,否则false
x as T
返回转换为类型 T 的 x ,如果 x 不是 T 则返回null
x == y
等于
x != y
不等于
x & y
整形按位与 ,布尔逻辑AND
`x
y`
x && y
且,当 x 为true
时,才对 y 求值
`x
x ?? y
如果 x 为null
,则计算结果为 y,否则为 x
三元
x ? y : z
如果 x 为true
,对 y 求值,x 为false
,对 z 求值
赋值或匿名函数
x = y
赋值
x = x + y
复合赋值
(T x) => y
匿名函数(lambda表达式)
条件判断
if (a == "aaa")
{
Console.WriteLine(a);
}
else if (a == "bbb")
{
Console.WriteLine(b);
}
else {
Console.WriteLine(c);
}
Switch
循环
/* for 循环执行 */
for (int a = 10; a < 20; a = a + 1)
{
Console.WriteLine("a 的值: {0}", a);
}
Console.ReadLine();
for each
int[] fibarray = new int[] { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in fibarray)
{
System.Console.WriteLine(element);
}
System.Console.WriteLine();
while
/* while 循环执行 */
while (a < 20)
{
Console.WriteLine("a 的值: {0}", a);
a++;
}
Console.ReadLine();
do while
/* do 循环执行 */
do
{
Console.WriteLine("a 的值: {0}", a);
a = a + 1;
} while (a < 20);
数组
//声明没有元素的数组
int[] ints = new int[6]
//声明初始化有元素的数组
int[] ints = new int[]{1, 3, 4, 5}
//在声明初始化有元素的数组时,也可以指定数组大小
string[] strings = new int[5]{"H", “E", "L",“L","0"}
访问修饰符
- public:所有对象都可以访问;
- private:对象本身在对象内部可以访问;
- protected:只有该类对象及其子类对象可以访问
- internal:同一个程序集的对象可以访问;Internal 访问说明符允许一个类将其成员变量和成员函数暴露给当前程序中的其他函数和对象。换句话说,带有 internal 访问修饰符的任何成员可以被定义在该成员所定义的应用程序内的任何类或方法访问。
- protected internal:访问限于当前程序集或派生自包含类的类型。
<Access Specifier> <Return Type> <Method Name>(Parameter List)
{
Method Body
}
Access Specifier:访问修饰符,这个决定了变量或方法对于另一个类的可见性。
Return type:返回类型,一个方法可以返回一个值。返回类型是方法返回的值的数据类型。如果方法不返回任何值,则返回类型为 void。
Method name:方法名称,是一个唯一的标识符,且是大小写敏感的。它不能与类中声明的其他标识符相同。
Parameter list:参数列表,使用圆括号括起来,该参数是用来传递和接收方法的数据。参数列表是指方法的参数类型、顺序和数量。参数是可选的,也就是说,一个方法可能不包含参数。
Method body:方法主体,包含了完成任务所需的指令集。
顾名思义,就是把变量值作为形参传了进去,方法内部如何折腾, 外面的变量a,b的值也不会发生改变,变动的只有swap方法内部的x,y的值
class NumberManipulator
{
public void swap(int x, int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 100;
int b = 200;
Console.WriteLine("在交换之前,a 的值: {0}", a);
Console.WriteLine("在交换之前,b 的值: {0}", b);
/* 调用函数来交换值 */
n.swap(a, b);
Console.WriteLine("在交换之后,a 的值: {0}", a);
Console.WriteLine("在交换之后,b 的值: {0}", b);
Console.ReadLine();
}
}
按引用传参(ref)
通过ref
修饰方法的入参,这种是按引用传参,传递的是某个实际变量的引用(即该对象的值所在的内存位置)
当按引用传递参数时,与值参数不同的是,它不会为这些参数创建一个新的存储位置。引用参数表示与提供给方法的实际参数具有相同的内存位置。
这种情况下传进去的引用,如果在方法内部对传入参数做了处理,那么最后结果也会影响到实际的变量,会改变变量的值
class NumberManipulator
{
public void swap(ref int x, ref int y)
{
int temp;
temp = x; /* 保存 x 的值 */
x = y; /* 把 y 赋值给 x */
y = temp; /* 把 temp 赋值给 y */
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 100;
int b = 200;
Console.WriteLine("在交换之前,a 的值: {0}", a);
Console.WriteLine("在交换之前,b 的值: {0}", b);
/* 调用函数来交换值 */
n.swap(ref a, ref b);
Console.WriteLine("在交换之后,a 的值: {0}", a);
Console.WriteLine("在交换之后,b 的值: {0}", b);
Console.ReadLine();
}
}
在交换之前,a 的值:100
在交换之前,b 的值:200
在交换之后,a 的值:200
在交换之后,b 的值:100
按输出传递参数 (out)
会把方法输出的值赋值给传入的参数,如下面最后我们设置的入参out int x
会被赋值,值为temp变量的值
class NumberManipulator
{
public void getValue(out int x )
{
int temp = 5;
x = temp;
}
static void Main(string[] args)
{
NumberManipulator n = new NumberManipulator();
/* 局部变量定义 */
int a = 100;
Console.WriteLine("在方法调用之前,a 的值: {0}", a);
/* 调用函数来获取值 */
n.getValue(out a);
Console.WriteLine("在方法调用之后,a 的值: {0}", a);
Console.ReadLine();
}
}
在方法调用之前,a 的值: 100
在方法调用之后,a 的值: 5
结构体 Struct
结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。
声明结构体
struct Books
{
public string title;
public string author;
public string subject;
public int book_id;
};
用法
struct Books
{
public string title;
public string author;
public string subject;
public int book_id;
};
public class testStructure
{
public static void Main(string[] args)
{
Books Book1; /* 声明 Book1,类型为 Books */
Books Book2; /* 声明 Book2,类型为 Books */
/* book 1 详述 */
Book1.title = "C Programming";
Book1.author = "Nuha Ali";
Book1.subject = "C Programming Tutorial";
Book1.book_id = 6495407;
/* book 2 详述 */
Book2.title = "Telecom Billing";
Book2.author = "Zara Ali";
Book2.subject = "Telecom Billing Tutorial";
Book2.book_id = 6495700;
/* 打印 Book1 信息 */
Console.WriteLine( "Book 1 title : {0}", Book1.title);
Console.WriteLine("Book 1 author : {0}", Book1.author);
Console.WriteLine("Book 1 subject : {0}", Book1.subject);
Console.WriteLine("Book 1 book_id :{0}", Book1.book_id);
/* 打印 Book2 信息 */
Console.WriteLine("Book 2 title : {0}", Book2.title);
Console.WriteLine("Book 2 author : {0}", Book2.author);
Console.WriteLine("Book 2 subject : {0}", Book2.subject);
Console.WriteLine("Book 2 book_id : {0}", Book2.book_id);
Console.ReadKey();
}
}
结构体特点
结构可带有方法、字段、索引、属性、运算符方法和事件。
结构可定义构造函数,但不能定义析构函数。但是,您不能为结构定义无参构造函数。无参构造函数(默认)是自动定义的,且不能被改变。
与类不同,结构不能继承其他的结构或类。
结构不能作为其他结构或类的基础结构。
结构可实现一个或多个接口。
结构成员不能指定为 abstract、virtual 或 protected。
当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。
如果不使用 New 操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用。
类和结构有以下几个基本的不同点:
类是引用类型,结构是值类型。
结构不支持继承。
结构不能声明默认的构造函数。
所以还可以声明如下结构体
struct Books
{
private string title;
private string author;
private string subject;
private int book_id;
public void setValues(string t, string a, string s, int id)
{
title = t;
author = a;
subject = s;
book_id =id;
}
public void display()
{
Console.WriteLine("Title : {0}", title);
Console.WriteLine("Author : {0}", author);
Console.WriteLine("Subject : {0}", subject);
Console.WriteLine("Book_id :{0}", book_id);
}
};
调用
Books Book1 = new Books(); /* 声明 Book1,类型为 Books */
Books Book2 = new Books(); /* 声明 Book2,类型为 Books */
/* book 1 详述 */
Book1.setValues("C Programming",
"Nuha Ali", "C Programming Tutorial",6495407);
/* book 2 详述 */
Book2.setValues("Telecom Billing",
"Zara Ali", "Telecom Billing Tutorial", 6495700);
/* 打印 Book1 信息 */
Book1.display();
/* 打印 Book2 信息 */
Book2.display();
枚举 enum
枚举是一组命名整型常量。枚举类型是使用 enum 关键字声明的。
C# 枚举是值类型。换句话说,枚举包含自己的值,且不能继承或传递继承。
枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。默认情况下,第一个枚举符号的值是 0
public class EnumTest
{
enum Day { Sun, Mon, Tue, Wed, Thu, Fri, Sat };
static void Main()
{
int x = (int)Day.Sun;
int y = (int)Day.Fri;
Console.WriteLine("Sun = {0}", x);
Console.WriteLine("Fri = {0}", y);
}
}
输出
Sun = 0
Fri = 5
接口
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分。
接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。
定义接口
interface IMyInterface
{
void MethodToImplement();
}
实现接口
using System;
interface IMyInterface
{
// 接口成员
void MethodToImplement();
}
class InterfaceImplementer : IMyInterface
{
static void Main()
{
InterfaceImplementer iImp = new InterfaceImplementer();
iImp.MethodToImplement();
}
public void MethodToImplement()
{
Console.WriteLine("MethodToImplement() called.");
}
}
类和对象
类的定义
类的定义是以关键字 class 开始,后跟类的名称。类的主体,包含在一对花括号内。下面是类定义的一般形式:
访问标识符 指定了对类及其成员的访问规则。如果没有指定,则使用默认的访问标识符。类的默认访问标识符是 internal,成员的默认访问标识符是 private。
数据类型 指定了变量的类型,返回类型
如果要访问类的成员,你要使用点(.)运算符。
点运算符链接了对象的名称和成员的名称。
<access specifier> class class_name
{
// member variables
<access specifier> <data type> variable1;
<access specifier> <data type> variable2;
...
<access specifier> <data type> variableN;
// member methods
<access specifier> <return type> method1(parameter_list)
{
// method body
}
<access specifier> <return type> method2(parameter_list)
{
// method body
}
...
<access specifier> <return type> methodN(parameter_list)
{
// method body
}
}
例如:
class Box
{
private double length; // 长度
private double breadth; // 宽度
private double height; // 高度
public void setLength( double len )
{
length = len;
}
public void setBreadth( double bre )
{
breadth = bre;
}
public void setHeight( double hei )
{
height = hei;
}
public double getVolume()
{
return length * breadth * height;
}
}
构造函数
类的 构造函数 是类的一个特殊的成员函数,当创建类的新对象时执行。
构造函数的名称与类的名称完全相同,它没有任何返回类型。
class Line
{
private double length; // 线条的长度
public Line()
{
Console.WriteLine("对象已创建");
}
继承
C#同样不可以多继承,只可以有一个父类,通过类似于子类 : 父类
的形式声明自己的继承类
但可以通过接口实现多重继承。一个类可以派生自多个类或接口,这意味着它可以从多个基类或接口继承数据和函数。
class Shape
{
public void setWidth(int w)
{
width = w;
}
public void setHeight(int h)
{
height = h;
}
protected int width;
protected int height;
}
// 派生类
class Rectangle: Shape
{
public int getArea()
{
return (width * height);
}
}
多态性
多态性意味着有多重形式。在面向对象编程范式中,多态性往往表现为"一个接口,多个功能"。
多态性可以是静态的或动态的。在静态多态性中,函数的响应是在编译时发生的。在动态多态性中,函数的响应是在运行时发生的。
可以在同一个范围内对相同的函数名有多个定义。函数的定义必须彼此不同,可以是参数列表中的参数类型不同,也可以是参数个数不同。不能重载只有返回类型不同的函数声明。
public int Add(int a, int b, int c)
{
return a + b + c;
}
public int Add(int a, int b)
{
return a + b;
}
抽象类
关键字 abstract 创建抽象类,用于提供接口的部分类的实现。当一个派生类继承自该抽象类时,实现即完成。抽象类包含抽象方法,抽象方法可被派生类实现。
- 不能创建一个抽象类的实例。
- 不能在一个抽象类外部声明一个抽象方法。
- 通过在类定义前面放置关键字 sealed,可以将类声明为密封类。当一个类被声明为 sealed 时,它不能被继承。抽象类不能被声明为 sealed。
实现方法时也会用到override关键字,但是与java不同的是,他不是一个注解,而是一个修饰符
abstract class Shape
{
abstract public int area();
}
class Rectangle: Shape
{
private int length;
private int width;
public Rectangle( int a=0, int b=0)
{
length = a;
width = b;
}
public override int area ()
{
Console.WriteLine("Rectangle 类的面积:");
return (width * length);
}
}
虚方法
当有一个定义在类中的函数需要在继承类中实现时,可以使用虚方法。
虚方法是使用关键字 virtual
声明的。
虚方法可以在不同的继承类中有不同的实现。
对虚方法的调用是在运行时发生的。
动态多态性是通过 抽象类 和 虚方法 实现的。
public class Shape
{
public int X { get; private set; }
public int Y { get; private set; }
public int Height { get; set; }
public int Width { get; set; }
// 虚方法
public virtual void Draw()
{
Console.WriteLine("执行基类的画图任务");
}
}
class Circle : Shape
{
public override void Draw()
{
Console.WriteLine("画一个圆形");
base.Draw();
}
}
重载运算符
重载运算符是具有特殊名称的函数,是通过关键字 operator 后跟运算符的符号来定义的。
public static Box operator+ (Box b, Box c)
{
Box box = new Box();
box.length = b.length + c.length;
box.breadth = b.breadth + c.breadth;
box.height = b.height + c.height;
return box;
}
上面的函数为用户自定义的类 Box 实现了加法运算符(+)。它把两个 Box 对象的属性相加,并返回相加后的 Box 对象。
所有内容仅限于维护网络安全学习参考