C#一些重要的功能
布尔条件
自动垃圾回收
标准库
组件版本
属性和事件
委托和事件管理
索引器
条件编译
简单的多线程
LINQ和Lambda表达式
集成Windows
C#程序主要包括一下部分
命名空间声明
一个class
Class方法
Class属性
一个Main方法
语句&表达式
注释
注:C#文件的后缀名为 .cs
程序调试的思路和详细步骤
设置断点:分析可能出现错误的位置,并设置断点
调试运行:启动调试,单步运行
观察变量:不断的观察特定变量的值
分析问题:通过观察变量的值,发现问题
修改代码:重新运行
需要程序调试的场合:
程序出现异常,无法直接找到错误的原因
简单的语句解释
Console.Write();//输出不换行
Console.WriteLine();//输出换行
Console.ReadLine();//读取键盘输入的所有字符,返回字符串。按下回车键退出
Console.Read();//读取键盘输入的第一个字符,返回其对应的ASCII值。按下回车键退出
Console.ReadKey();//等待用户按下任意键并执行退出(此函数的作用是为了在控制台窗口停留一下
//直到用户敲击键盘为止,不然运行时,控制台窗口一闪而过,没法查看)
简单的程序组成分析解释
using System;
namespace ChengXun
{
class HelloWorld
{
static void Main(string[] args)
{
//输出Hello World
Console.WriteLine("Hello World")
Console.ReadKey();
}
}
}
1.using 关键字用于在程序中包含System命名空间(一个程序可以有多个using语句)
2.namespace 声明。一个namespace里包含一系列的类,ChengXun命名空间包含类类 HelloWprld
3.class声明。类HelloWorld包含程序使用的数据和方法声明。类一般包含多个方法,方法定义了类的行为。在这里,Hello类只有一个Main方法。
4.定义了Main方法,是所有C#程序的入口点。Main方法说明当执行时,类将做什么动作
Main方法通过语句Console.WroteLine(“Hello World”);指定了它的行为
WriteLine是一个定义在System命名空间中的Console类的一个方法。
该语句会在屏幕上显示消息“Hello World”
5.Console.ReadKey();等待一个按键动作,防止程序启动时屏幕快速关闭
注:在C#中严格区分大小写
对象或类的方法、属性调用必须放在方法中
一个程序只能有一个Main() 方法返回值命令行参数可选,程序的执行从Main方法开始
字符串的值必须使用双引号括起来
所有的语句和表达式必须以分号(;)结尾
注:注释会被编译器忽略
单行注释://
多行注释:/* */
文档注释:///
代码折叠器:#region
#endregion(必须成对使用)
2023.8.31 17:18
using System;
namespace C._9_5
{
class Juxing
{
double chang;
double kuan;
public void FuZhi()
//public公有字段,外部可见,也称成员函数
{
//chang=4.5;//直接赋值
//kuan=3.5;
Console.Write("请分别输出长和宽(回车分隔):");//提示输入
chang = Convert.ToDouble(Console.ReadLine());//键盘输入,万能转换,键盘输入是string类型
kuan = Convert.ToDouble(Console.ReadLine());
}
public double MianJi()
{
return chang * kuan;//面积公式
}
public void ShuChu()
{
Console.WriteLine("矩形的长:{0}", chang);//输出信息
Console.WriteLine("矩形的宽:{0}",kuan);
Console.WriteLine("矩形的总面积:{0}", MianJi());
}
}
class Program
{
static void Main(string[] args)//程序入口
{
Juxing r = new Juxing();//类实例化
r.FuZhi();//调用类(Juxing)中的成员函数(FuZhi)
r.ShuChu();
Console.ReadKey();
}
}
}
using 关键字
在任何C#程序中的第一条语句都是:using System;(类似)
成员变量
变量是类的属性和数据成员,用于存储数据。在上面程序中,Juxing类有两个成员变量,名为chang和kuan。
成员函数
函数是一系列执行指定任务的语句。类的成员函数在类内声明。以上程序,Juxing类,包含了成员函数:FuZhi、MianJi、ShuChu。
注: C#中关键字不能用作标识符
2023.9.5 14:08
C#数据类型
值类型(Value types)
值类型变量可以直接分配一个值,值类型直接包含数据,比如int、char,分别存储数字和字符
使用 sizeof 方法,可以直接查询到对应的字节
Console.WriteLine("{0}", sizeof(int));
引用类型
不包含存储变量中的实际数据,但包含对变量的引用。指的是一个内存的位置
引用类型用的是同一个地址,值类型是另外开辟一个空间
使用多个变量时,引用类型可以指向内存位置,内置引用类型有:object、dynamic和string
对象(Object)类型
是C#通用类型系统中所有数据类型的终极基类
所以对象Object类型可以被分配任何其他类型,但是在分配值之前,需要先进行类型转换
当一个类型转换为对象类型时,则被称为装箱。
反之,当一个对象类型转换为值类型时,则称为拆箱
int val = 8;
object obj = val;//先装箱
int nval = (int)obj;//再拆箱
只有装过箱的数据才可以拆箱
动态类型
可以存储任何类型的值在动态数据类型变量中,变量的类型检查是在运行时发生的
声明动态类型的语法:
dynamic < variable_name > = value;
动态类型 名字 = 赋值;
dynamic d = 20;
动态类型与对象类型相似
对象类型变量的类型检查是在编译时发生的
动态类型变量的类型检查是在运行时发生的
字符串(string)类型
允许给变量分配任何字符串值。可以通过两种形式进行分配:引号和@引 号
String str = "runoob.com";
C# string 字符串的前面可以加 @(称作"逐字字符串")将转义字符(\)当作普通字符对待
String str = @"C:\Windows";
@字符串中可以任意换行,换行符及缩进空格都计算在字符串长度之内
用户自定义引用类型有:class、interface 或 delegate
指针类型
指针类型变量存储另一种类型的内存地址。
声明指针类型的语法:
type* identifier;
类型* 识别码?;
char* cptr;
int* iptr;
C#类型转换
小转大,隐形转换,精度不会丢失
大转小,显性转换,进度会丢失
byte a = 1;//小
int b = a;//转大
Console.WriteLine(b);//结果为1
double c = 2.4444;//大
int d = (int)c;//转小,精度丢失(强制转换)
Console.WriteLine(d);//结果为2
Console.WriteLine("输入一串数字,否则报错");
double too = Convert.ToDouble(Console.ReadLine());
//万能转换,键盘输入是字符串(string)类型,转换为double类型
int t = 12;
double p = 1.222;
float g = 2.49877f;
bool jj = true;
Console.WriteLine(t.ToString());
Console.WriteLine(p.ToString());
Console.WriteLine(g.ToString());
Console.WriteLine(jj.ToString());
Console.ReadKey();
- 对于对象类型的转换,需要进行类型转换的兼容性检查和类型转换的安全性检查。
使用 int 强制转换为整数,并不存在四舍五入的情况,而是直接将后面的小数位数丢掉
Convert.ToInt32() 采取的取舍是进行四舍五入,而 (int) 则是截取浮点型的整数部分
是数字类型可以考虑直接用(int)强制转换
如果是整型字符串类型的,考虑用 int.Parse() 进行转换
如果不是这两种类型,再考虑用 Convert.ToInt32() 进行转换。
关于溢出
将大的数据类型转换为小的数据类型时 Convert.ToInt32() 和 int.Parse() 都会报溢出错误,值对于 Int32 太大或太小,而 (int) 不报错,但是返回值为 -1。
int r = 322;
long t = r;//隐形转换
class1 g = new class2();
// 这里也是隐式转换,将一个新建的 Class2 实例转换为了其基类 Class1 类型的实例 C1
//???不明白
string locstr = 123.ToString();
//如果要将"locstr"转成整型数
//方法一: 用 Convert
int i = Convert.ToInt16(locstr);
//方法二: 用 Parse
int ii = int.Parse(locstr);
//方法三:用TryParse
//int.TryParse(string s,out int i)
//最后一个参数是输出值,如果转换成功则输出相应的值,否则输出0
string s1 = "gjag";
string s2 = "6817";
int a, b;
bool boK = int.TryParse(s1, out a);//结果:gjag False 0
Console.WriteLine(s1 + " " + boK + " " + a);
bool boL = int.TryParse(s2, out b);//结果:6817 true 6817
Console.WriteLine(s2 + " " + boL + " " + b);
int无法直接转换string,需要用到int.Parse(locstr)
try:尝试下面代码
catch:如果出现异常,解决方案,或者提示
try
{
Console.Write("键盘输入一个数值,计算加1的结果:");
int gg = int.Parse(Console.ReadLine());//可能抛出异常
Console.WriteLine("答案是{0}",++gg);
}
catch(Exception)//异常错误
{
Console.WriteLine("无法转换");
}
C#变量
C#中变量的定义
<data_type> <variable_list>;
data_type必须是有效的数据类型,variable_list可以是一个或多个以逗号分隔的的标识符名称组成
int a0,a1;
float b;
double c;
char cc;
//变量初始化
int a = 20;
//定义后,在赋值也可以
在变量之前加上 static 关键字,即可声明为静态变量
static double gg1 = 20.3;
2023/9/26 11:47
C#常量
常量是固定值,程序执行期间不会改变。常量可以是任何基本数据类型
常量可以被当作常规的变量,只是它们的值在定义后不能被修改。
整数常量
0x 或 0X 表示十六进制,0 表示八进制,没有前缀则表示十进制
可以是 U 和 L 的组合,其中,U 和 L 分别表示 unsigned 和 long
整数常量的实例:
85 /* 十进制 */
0213 /* 八进制 */
0x4b /* 十六进制 */
30 /* int */
30u /* 无符号 int */
30l /* long */
30ul /* 无符号 long */
浮点常量的实例:
3.14159 /* 合法 */
314159E-5L /* 合法 */
510E /* 非法:不完全指数 */
210f /* 非法:没有小数或指数 */
.e55 /* 非法:缺少整数或小数 */
使用浮点形式表示时,必须包含小数点、指数或同时包含两者!
有符号的指数是用 e 或 E 表示的。
#运算符
sizeof() 返回数据类型的大小。例:sizeof(int),将返回4
typeof()返回class的类型。 例:typeof(Streamreader);
//typeof 关键字用于获取一个类型的类型对象,它通常用于反射和动态创建类型实例
Type gg = typeof(string);
Console.WriteLine(gg.FullName);
Console.ReadKey();
上面的代码中,使用typeof关键字来获取string类型的类型对象,并将其存储在TYpe类型的变量gg中,使用FullName属性打印该类型的完全限定名
输出的结果为:System.String
& 返回变量的地址 例:&a;将得到变量的实际地址。
* 变量的指正 例:*a,将指向一个变量。
?: 条件表达式 例:如果条件为真?则为X:否则为Y
is 判断对象是否为某一类型 例:if(Ford is Car)//检查Ford是否是Car类的一个对象
as 强制装换,即使装换失败也不会抛出异常
例:object obj= new StringReader("Hello");
StringReader r = obj as StringReader;
C#判断
if语句:一个if语句由一个布尔表达式后跟一个或多个语句组成
if...else语句:一个if语句后壳跟一个可选的else语句,else语句在布尔表达式为假时执行
一个if语句后可跟一个可选的else if...else语句,可用于测试多种条件
使用时有几点注意:1.一个if后可跟0或1个else,它必须在任何一个else if之后。
2.一个if后后跟0个或多个else if,它们必须再else之前
3.一旦某个else if 匹配成功,其他的else if 或else将不会被测试
if(boolean_expression 1)
{
/* 当布尔表达式 1 为真时执行 */
}
else if( boolean_expression 2)
{
/* 当布尔表达式 2 为真时执行 */
}
else if( boolean_expression 3)
{
/* 当布尔表达式 3 为真时执行 */
}
else
{
/* 当上面条件都不为真时执行 */
}
嵌套if语句:可以在if或else if 语句内使用另一个if或else if语句
if( boolean_expression 1)
{
/* 当布尔表达式 1 为真时执行 */
if(boolean_expression 2)
{
/* 当布尔表达式 2 为真时执行 */
}
}
switch语句:一个switch语句允许测试一个变量等于多个值时的情况。每个值称为一个case,且被测试的变量会对每个switch case进行检查
using System;
namespace MyApplication
{
class Program
{
static void Main(string[] args)
{
int day = 4;
switch (day)
{
case 1:
Console.WriteLine("Monday");
break;
case 2:
Console.WriteLine("Tuesday");
break;
case 3:
Console.WriteLine("Wednesday");
break;
case 4:
Console.WriteLine("Thursday");
break;
case 5:
Console.WriteLine("Friday");
break;
case 6:
Console.WriteLine("Saturday");
break;
case 7:
Console.WriteLine("Sunday");
break;
}
}
}
}
1.switch语句中的expression必须是一个整型或枚举类型,或者是一个class类型,强制class有一个单一的转换函数讲起转换为整型或者枚举类型。
2.在switch中可以有任意数量的case语句,每个case后跟一个要比较的值和一个冒号
3.case的constant...必须与switch中的变量具有相同的数据类型,且是一个常量。变量等于其中一个case中的常量是,case后的语句讲被执行。直到遇到break语句为止。不是每个case语句都包含break为止。如果case语句为空,则可以不包含break
4.遇到break语句时,switch终止,控制流将跳转switch语句后的下一行
5.一个switch语句可以有一个可选的default语句,在switch的结尾。default语句用于在上面室友的case都都不为true时执行的一个任务。default也需要包含break语句。
C#不允许从一个case部分继续执行到下一个case部分。如果有已经在执行的case语句,则必须包含break或其他跳转语句。
using System;
namespace DecisionMaking
{
class Program
{
static void Main(string[] args)
{
/* 局部变量定义 */
char grade = 'B';
switch (grade)
{
case 'A':
Console.WriteLine("很棒!");
break;
case 'B':
case 'C':
Console.WriteLine("做得好");
break;
case 'D':
Console.WriteLine("您通过了");
break;
case 'F':
Console.WriteLine("最好再试一下");
break;
default:
Console.WriteLine("无效的成绩");
break;
}
Console.WriteLine("您的成绩是 {0}", grade);
Console.ReadLine();
}
}
}
嵌套switch语句:可以吧一个switch作为一个外部switch的语句序列的一部分,既可以在一个switch语句内使用另一个switch语句。即使内部和外部switch的case常量包含共同的值,也没有矛盾
using System;
namespace DecisionMaking
{
class Program
{
static void Main(string[] args)
{
int a = 100;
int b = 200;
switch (a)
{
case 100:
Console.WriteLine("这是外部 switch 的一部分");
switch (b)
{
case 200:
Console.WriteLine("这是内部 switch 的一部分");
break;
}
break;
}
Console.WriteLine("a 的准确值是 {0}", a);
Console.WriteLine("b 的准确值是 {0}", b);
Console.ReadLine();
}
}
}
?:运算符,可以代替if...else语句 形式:Exp1?Exp2:Exp3;
其中Exp1/2/3是表达式 注:冒号的使用和位置
?表达式的值时由Exp1决定的。如果Exp1为真,则计算Exp2的值,结果即为整个?表达式的值。如果Exp1为假,则计算Exp3的值,结果即为整个?表达式的值
C#循环
while循环:只要给定的条件为真,C#中的while循环语句会重复执行一个目标语句
using System;
namespace Loops
{
class Program
{
static void Main(string[] args)
{
//局部变量定义
int a= 10;
//whilr 循环执行
while(a<20)//a小于20进入循环
{
Console.WriteLine(“a 的值:{0}”,a);
a++;//最后加1
}
Console.ReadLine();
}
}
}
for/foreach循环:允许编写一个执行特定次数的循环的重复控制结构
C#中for循环的语法:
for(init;condition;increment)
{
statement(s);
}
for循环的控制流:
1.init会首先被执行,且只会执行一次。允许声明并初始化任何循环控制变量,也可以补写任何语句,只要有个分号出现即可。
2.会判断condition。如果为真,则执行循环主体。如果为假,则不执行循环主体。且控制流会跳转到紧接着for循环的下一条语句
3.执行完for循环主体后,控制流跳回上面的increment语句。该语句允许更新循环控制变量。该语句可以留空,条件后有个分号出现即可。
4.条件再次被判断。如果为真,则执行循环,过程会不断重复(循环主体,然后增加步值,再然后重新判断条件)在条件为假时,for循环终止
using System;
namespace Loops
{
class Program
{
static void Main(string[] args)
{
/* for 循环执行 */
for (int a = 10; a < 20; a = a + 1)
{
Console.WriteLine("a 的值: {0}", a);
}
Console.ReadLine();
}
}
}
foreach循环:可以用来遍历集合类型,例如数组列表、字典等。是一个简化版的for循环
foreach (var item in collection)
{
// 循环
}
conllection是遍历的集合,iten是当前遍历到的元素
实例三个部分:
通过foreach循环输出整型数组中的元素
通过for循环输出整型数组中的元素
foreach循环设置数组元素的计算器
using System;
namespace _10_28
{
class Program
{
static void Main(string[] args)
{
int[] fibarray = new int[] { 0, 1, 1, 2, 3, 5, 8, 13 };
foreach (int element in fibarray)//1.
{
Console.WriteLine(element);
}
Console.WriteLine();
//类似foreach循环
for (int i = 0; i < fibarray.Length; i++)//2.输出结果一样
{
Console.WriteLine(fibarray[i]);
}
//设置集合中的元素的计算器
int count = 0;
foreach (int element in fibarray)
{
count += 1;
//count值反映了循环主体的执行次数,从1开始代表了数组中第一个整型,依次往后
Console.WriteLine("元素序号:{0} 元素:{1}", count, element);
}
Console.WriteLine("Number of elements in the array:{0}", count);
}
}
}
do...while循环:是在循环的尾部检查它的条件,确保至少执行一次循环
using System;
namespace Loops
{
class Program
{
static void Main(string[] args)
{
/* 局部变量定义 */
int a = 10;
/* do 循环执行 */
do
{
Console.WriteLine("a 的值: {0}", a);
a = a + 1;
} while (a < 20);//判断条件是否为真?
Console.ReadLine();
}
}
}
嵌套循环:C#允许在一个循环内使用另一个循环。
using System;
namespace _11_3
{
class Program
{
static void Main(string[] args)
{
//局部变量定义
int i, j;
for (i = 3; i < 100; i++)
{
for (j = 2; j <= (i / j); j++)
if ((i % j) == 0) break;
if (j > (i / j))
Console.WriteLine("{0}是质数", i);
}
Console.ReadKey();
}
}
}
循环控制语句:更改执行的正常序列,当执行离开一个范围时。所有在改范围中创建的自动对象都会被销毁,C#提供了下列控制语句!
break语句:当一个循环内出现break语句时,循环会立即终止,且程序流将继续执行紧接的循环下一条语句。可用于终止switch语句中的一个case,如果使用的是嵌套循环,break语句会停止执行最内层的循环,然后开始执行改块之后的下一行代码
using System;
namespace _11_3
{
class Program
{
static void Main(string[] args)
{
//局部变量定义
int a = 10;
//while 循环执行
while(a<20)
{
Console.WriteLine("a的值:{0}", a);
a++;
if(a>15)
{
//使用break语句终止
//a大于15时,进入该语句,终止循环
break;
}
}
Console.ReadKey();
}
}
}
continue语句:跳过本轮循环,强迫开始下一轮循环
对于for循环,continue语句会导致执行条件测试增量部分。对于while和do...while循环,continue语句会导致很想控制回到条件测试上
using System;
namespace ggg
{
class Program
{
static void Main(stringp[] args)
{
int a = 10;
do
{
if (a == 15)
{
a = a + 1;
continue;//等于15时,跳出整个循环再次判断
}
Console.WriteLine("a的值:{0}", a);
a++;
} while (a < 20);
Console.ReadLine();
}
}
}
C#封装:
被定义为把一个或多个项目封闭在一个物理的或者逻辑的包中。在面向对象程序设计方法论中,封装是为了防止对实现的访问。
抽象和封装是面向对象程序设计的相关特性。抽象允许相关信息可视化,封装则使开发者实现所需级别的抽象
C#封装根据具体的需要,设置使用者的访问权限,并通过访问修饰符来实现:
public:所有对象都可以访问
访问修饰符允许一个类将其成员变量和成员函数暴露给其他的函数和对象。任何公有成员可以被外部的类访问
using System;
namespace _11_6
{
class guggg
{
public double chang;//被声明为public,所以可以被实例r访问
public double kuan;
//被声明为public,所以可以被实例r访问
public double GetArea()//同一个类里可以直接访问这些变量,
{
return chang * kuan;
}
public void shuchu()//同一个类里可以直接访问这些变量
{
Console.WriteLine("长:{0}", chang);
Console.WriteLine("宽:{0}", kuan);
Console.WriteLine("面积:{0}", GetArea());
}
}
class Program
{
static void Main(string[] args)
{
guggg r = new guggg();
r.chang = 4.5;
r.kuan = 3.5;
r.shuchu();
Console.ReadKey();
}
}
}
private:对象本身在对象内部可以访问;
允许一个类将其成员变量和成员函数对其他的函数和对象进行隐藏。只有同一个类中的函数可以访问它的私有成员。即使是类的实例也不能访问它的私有成员
using System;
namespace _11_6
{
class tttge
{
private double chang;//被private声明,所以不能直接实例访问
private double kuan;
public void zhi()//同一个类可以被访问
{
Console.WriteLine("请输入长度:");
chang = Convert.ToDouble(Console.ReadLine());
Console.WriteLine("请输入宽度:");
kuan = Convert.ToDouble(Console.ReadLine());
}
public double GetArea()
{
return chang * kuan;
}
public void shuchu()
{
Console.WriteLine("长:{0}", chang);
Console.WriteLine("宽:{0}", kuan);
Console.WriteLine("面积:{0}", GetArea());
}
}
class Program
{
static void Main(string[] args)
{
tttge t = new tttge();
t.zhi();
t.shuchu();
Console.ReadKey();
}
}
}
protected:只有该类对象及其子类对象可以访问
允许子类访问它的基类的成员变量和成员函数,有助于实现继承
internal:同一个程序集的对象可以访问;
允许一个类将其成员变量和成员函数暴露给当前程序中的其他函数和对象。换句话说,带有internal访问修饰符的任何成员可以被定义在该成员所定义的应用程序内的任何类或方法访问
using System;
namespace _11_6
{
class inntt
{
internal double chang;
internal double kuan;
double GetArea()//声明的时候不带有任何访问修饰符,则默认为private
{
return chang * kuan;
}
public void shuchu()
{
Console.WriteLine("长:{0}", chang);
Console.WriteLine("宽:{0}", kuan);
Console.WriteLine("面积:{0}", GetArea());
}
}
class Program
{
static void Main(string[] args)
{
inntt i = new inntt();
i.chang = 4.5;
i.kuan = 3.5;
i.shuchu();
Console.ReadKey();
}
}
}
protected internal:访问限于当前程序集或派生自包含类的类型
允许在本类,派生类或者包含该类的程序集中访问,也被用于实现继承
大哥们的说法:
C#方法
一个方法是把一下相关的语句组织在一起,用来执行一个任务的语句块。每个程序至少有一个带有Main方法的类。
1.定义方法
2.调用方法
定义一个方法时,从根本上说是在声明它的结构的元素。在C#中,定义方法的语法如下:
<Access Specifier><Return Type><Method Name>(Parameter List)
{
Method Body
}
Access Specifier:访问修饰符,决定了变量或者方法对于另一个类的可见性
Return type:放回类型,一个方法可以返回一个值。返回类型是方法返回的值的数据类型。如果方法不返回任何值,则返回类型为void
Mwthod name:方法名称,是一个为唯一的标识符,且是大小写敏感的。它不能与类中声明的其他标识符相同。
Parameter list:参数列表,使用圆括号括起来,该参数是用来传递和接收方法的数据。参数列表是指方法的参数类型、顺序和数量、参数是可选的,也就是说,一个方法可能不包含参数
Method body:方法主体,包含了完成任务所需的指令集
函数FindMax,它接受两个整数值,并返回两个中的较大值。有public访问修饰符,可以使用类的实例从类的外部进行访问
using System;
namespace _11_6.fan
{
class Number
{
public int FindMax(int num1,int num2)//接收两个值,并返回两个中较大值
{
int result;
if (num1 > num2)
result = num1;
else
result = num2;
return result;
}
}
class jiesheng
{
public int factor(int num)
{
int result;
if(num==1)
{
return 1;
}
else
{
result = factor(num - 1) * num;
return result;
}
}
}
class Program
{
static void Main(string[] args)
{
int a = 100;
int b = 200;
int ret;
//调用方法
Number r = new Number();//同一个类里,也是这样调用
ret = r.FindMax(a, b);
Console.WriteLine("最大值是:{0}", ret);
Console.ReadLine();
}
}
}
C#可空类型(Nallable)
nullable类型(可空类型),可空类型可以表示其基础值类型正常范围内的值,再加上一个null值
例如:Nullable<Int32>,读作“可空的int32"。可以被赋值为-2147483648到2147483647之间的任意值,也可以被赋值为null值。类似的,Nullable<bool>变量可以被赋值为true或false或null
在处理数据库和其他包含可能为赋值的元素的数据类型,将null赋值给数值类型或布尔型的功能特别有用,例如,数据库中的布尔型字段可以存储值true或false,或者,该字段也可以未定义
声明一个nullable类型(可空类型)的语法:
<data_type>?<variable_name>=null;
可空数据类型的用法:
using System;
namespace CalculatorApplication
{
class NullablesAtShow
{
static void Main(string[] args)
{
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);
Console.WriteLine("一个可空的布尔值:{0}",boolval);
Console.RedLine();
}
}
}
Null合并运算符(??):
用于定义可空类型和引用类型的默认值,Null合并运算符未类型转换定义了一个预设值,以防可空类型的值未Null。Null合并运算符把操作数类型隐式转换为另外一个可空(或不可空)的值类型的操作数的类型。如果第一个操作数的值为null,则运算符返回第二个操作数的值,否则返回第一个操作数的值。
using System;
namespace _11_13xin
{
class Program
{
static void Main(string[] args)
{
double? num1 = null;
double? num2 = 3.14157;
double num3;
num3 = num1 ?? 5.34;//num1 如果为空值则返回5.34
Console.WriteLine("num3的值{0}", num3);
num3 = num2 ?? 5.34;
Console.WriteLine("num3的值:{0}", num3);
Console.ReadLine();
}
}
}
C#种两个问号的作用是判断??左边的对象是否为null,如果不为null则使用??左边的对象,如果为null则使用??右边的对象
a=b??c,如果b为null,则a=c,如果不为null,则a=b
C#数值
数组是一个存储相同类型元素的固定大小的顺序集合。数组是用来存储数据的集合,通常认为数组是一个同一个类型变量的集合
所有的数组都是由连续的内存位置组成的。
最低的地址对应第一个元素,最高的地址对应最后一个元素
在C#中声明一个数组,数组的语法:
datatype[] arrayName;
其中datatype用于指定被存储在数组中的元素的类型
[ ] 指定数组的秩(维度),秩指定数组的大小
arrayName:数组的名称
double[] balance;
初始化数组
声明一个数组不会在内存中初始化数组。当初始化数组变量时,可以赋值给数组
数组是一个引用类型,需要使用new关键字来创建数组的实例
double[ ] balance = new double[10]
赋值给数组
//通过使用索引号赋值给一个单独的数组元素
double[] balance = new double[10]
balance[0]=45.99;
//声明数组的同时给数组赋值
double[] balance = {32.3,45.9,87.5};
//创建并初始化一个数组
int [] marks = new int[5] {66,24,68,44,34};
//也可以省略数组的大小
int [] marks = new int[] {44,37,58,28};
//可以赋值一个数组变量到另一个目标数组变量中。目标和源会指向相同的内存位置
int [] marks = new int[] {92,45,98,58};
int[] score =marks;
创建一个数组时,C#编译器会根据数组类型隐式初始化每个数组元素为一个默认值,
例如,int数组的所有元素都会被初始化为0
访问数组元素
元素是通过带索引的数组名称来访问的,这是通过把元素的索引放置在数组名称后的方括号来实现
double salary = balance[9];
声明、赋值、访问数组例子:
using System;
namespace _11_13shuzu
{
class Program
{
static void Main(string[] args)
{
int[] n = new int[10];//n是一个带有10个的整数的数组
int i, j;
//初始化数组n中的元素
for(i=0;i<10;i++)
{
n[i] = i + 100;
}
//输出每个数组元素的值
for(j=0;j<10;j++)
{
Console.WriteLine("Element[{0}]={1}", j, n[j]);
}
Console.ReadKey();
}
}
}
前面使用for循环来访问每个数组,也可以使用foreach语句来遍历数组
foreach循环:
using System;
namespace _11_13shuzu
{
class Program
{
static void Main(string[] args)
{
int[] n = new int[10];//n是一个带有10个的整数的数组
//初始化数组n中的元素
for(int i=0;i<10;i++)
{
n[i] = i + 100;
}
//输出每个数组元素的值
foreach(int j in n)
{
int i = j - 100;
Console.WriteLine("Element[{0}]={1}", i,j);
}
Console.ReadKey();
}
}
C#多维数组
多维数组又称为矩形数组,声明一个string变量的二维数组string [,] names;
或者声明一个int变量的三维数组 int [, ,]m;
二维数组在本质上是一个一堆数组的列表,被认为是一个带有x行和y列的表格
数组中的每个元素使用形式为a[ i , j ]的元素名称来标识的,a是数组名称,i和j是唯一标识a中的每个元素的下标。可以通过在括号内为每行指定值来进行初始化
//定义二维数组
int[,] a = new int[3, 4]//3行4列
{
{0,1,2,3 },//初始化索引号为0的行
{4,5,6,7 },
{8,9,10,11 },
};
int val = a[2, 3];//访问索引号从0开始计数
Console.WriteLine(val);//输出结果为11
嵌套循环处理二维数组
int[,] a = new int[5, 2]
{
{0,0},
{1,2},
{3,4},
{5,6},
{7,8},
};
int i, j;
for(i=0;i<5;i++)
{
for (j = 0; j < 2; j++)
{
Console.WriteLine("a[{0},{1}]={2}", i, j, a[i, j]);//索引号,对应的数据
//比如a[2,1],就是上面二维数组的3行2列的4
}
}
遍历多维数组,三维数组,三重循环
//定义一个三维的数组
int[,,] t = new int[2, 2, 3]//2行2列,三维数组
{
{{1,2,3},{4,5,6} },
{{7,8,9},{10,11,12}}
};
int rank = t.Rank;//输出是几维数组
Console.WriteLine("该多维数组的维数为:{0}", rank);
int rlength = t.GetLength(1);//获得第二维的元素长度(列数)
//GetLength(0)获得第一维的元素长度(行数)
Console.WriteLine("该多维数组的第二维有{0}个元素", rlength);
Console.WriteLine("开始遍历多维数组");
Console.WriteLine("-----------------------------");
int wei = 0;
for(int i=0;i<t.GetLength(0);i++)
{
for(int j=0;j<t.GetLength(1);j++)
{
for(int j1 = 0; j1 < t.GetLength(1); j1++)
{
Console.WriteLine("最低维度{0}的值为{1}",wei,t[i, j, j1]);
}
++wei;
}
}
输出
C#交错数组
交错数组是数组的数组,是一堆数组,可以声明一个带有int类型的交错数组
int [] [] scores;
声明一个数组不会在内存中创建数组,初始化一个交错数组
这是由一个整型数组组成的数组---score[0]是一个带有3个整数的数组,scores[1]是一个带有4个整型的数组
int[][]scores = new int[2][]
{
new int[]{92,93,94},
new int[]{87,87,81,89}
};
交错数组与二维数组的区别,可以理解为交错数组每一行的长度是可以不一样的。
二维数组像是唐诗,那交错数组就是宋词,每一句的的长短没有限制
//声明一个交错数组,a,a中有三个元素,分别是a[0],a[1],a[2].每个元素都是一个数组
int[][] b = new int[3][];
//以下是声明交错的数组的每一个元素,每一个数组的长度是可以不同的
b[0] = new int[] { 1, 2, 3 };
b[1] = new int[] { 4, 5, 6, 7, 8, 9 };
b[2] = new int[] { 10,11,12,13 };
C#传递数组给函数
可以通过指定不带索引的数组名称来给函数传递一个指向数组的指针
using System;
namespace _11_13shuzu
{
class Program
{
#region 传递数组给函数
double getAverage(int[] arr, int size)
{
int i;
double avg;
int sum = 0;
for(i=0;i<size;++i)
{
sum += arr[i];
}
avg = (double)sum / size;
return avg;
}
static void Main(string[] args)
{
Program t = new Program();
//一个带有5个元素的int数组
int[] balance = new int[] { 1000, 2, 3, 15, 49 };
double avg;
//传递数组的指针作为参数
avg = t.getAverage(balance, 5);
//输出返回值
Console.WriteLine("平均值是:{0}", avg);
Console.ReadKey();
}
}
}
C#参数数组
当声明一个方法时,不能确定要传递给函数作为参数数目。参数数组通常用传递未知数量的参数给函数,在使用数组作为形参时,c#提供了params关键字,使调用数组为形参的方法时,即可以传递数组的实参,也可以传递一组数组的元素。使用格式为:
public 放回类型 方法名称(params 类型名称[] 数组名称)
using System;
namespace ArrayApplication
{
class ParamArray
{
public int AddElements(params int[] arr)
{
int sum = 0;
foreach (int i in arr)
{
sum += i;
}
return sum;
}
}
class TestClass
{
static void Main(string[] args)
{
ParamArray app = new ParamArray();
int sum = app.AddElements(512, 720, 250, 567, 889);
Console.WriteLine("总和是: {0}", sum);
Console.ReadKey();
}
}
}
带params关键字的参数类型必须是一堆数组,不能使用在多维数组上;
不允许和ref和out同时使用;
带params关键字的参数必须是最后一个参数,并且在方法声明中只允许一个params关键字
不能仅使用params来使用重载方法
没有params关键字的方法的优先级高于带有params关键字的方法的优先级
using System;
namespace MyParams
{
class MyParams
{
static void Test(int a, int b)
{
Console.WriteLine("a + b = {0}", a + b);
}
static void Test(params int[] list)
{
foreach(int i in list)
{
Console.Write("{0} ", i);
}
}
static void Main(string[] args)
{
Test(1, 2);
Test(1, 2, 3, 4, 5);
Console.ReadLine();
}
}
}
c#Array类
是C#中所有数组的基类,它是在System命名空间中定义。Array类提供了各种用于数组的属性和方法,常用的属性:
IsFixedSize: | 获取一个值,该值指示数组是否带有固定大小 |
IsReadOnly: | 获取一个值,该值指示数组是否只读 |
Length: | 获取一个32位整型,该值表示所有维度的数值中的元素总数 |
LongLength: | 获取一个64位整数,该值表示所有维度的数组中的元素总数 |
Rank: | 获取数组的秩(维度) |
Array类的方法,常用:
Clear: | 根据元素的类型,设置数组中某个范围的元素为零、为false或者为null |
Copy(Array,Array,Int32): | 从数组的第一个元素开始复制某个范围的元素到另一个数组的第一个元素位置。长度由一个32位整型指定 |
CopyTo(Array,Int32): | 从当前的一堆数组中复制所有的元素到一个指定的一堆数组的指定索引位置。索引由一个32位整数指定 |
GetLength: | 获取一个32位整数,该值表示指定维度的数组中的元素总数 |
GetLongLength: | 获取一个64位整数,该值表示指定维度的数组中的元素总数 |
GetLowerBound: | 获取数组中指 定维度的下界 |
GetType: | 获取当前实例的类型。从对象(Object)继承 |
GetupperBound: | 获取数组中指定维度的上界 |
GetValue(Int32): | 获取一堆数组中指定位置的值,索引由一个32位整数指定 |
indexOf(Array,Object): | 搜索指定的对象,返回整个一维数组中第一次出现的索引 |
Reverse(Array): | 逆转整个一维数组中元素的顺序 |
SetValue(Object,Int32): | 给一维数组中指定位置的元素设置值。索引由一个32位整数指定 |
Sort(Array): | 使用数组的每个元素的IComparable实现来排序整个一维数组中的元素 |
Tostring: | 返回一个表示当前对象的字符串。从对象(Object)继承 |
using System;
namespace _11_23Array
{
class Program
{
static void Main(string[] args)
{
int[] list = { 34, 72, 13, 44, 25, 30, 10 };
Console.Write("原始数组: ");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.WriteLine();
//逆转数组
Array.Reverse(list);
Console.Write("逆转数组:");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.WriteLine();
//排序数组
Array.Sort(list);
Console.Write("排序数组:");
foreach (int i in list)
{
Console.Write(i + " ");
}
Console.ReadLine();
}
}
}
C#字符串(string)
string是关键字是System.String类的别名,可以通过以下方法之一创建string对象
1.给String变量指定一个字符串 |
2.使用String类构造函数 |
3.使用字符串串联运算符 |
4.检索属性或调用一个返回字符串的方法 |
5.格式化方法来转换一个值或对象为它的字符串表示 |
String类的属性
常用
1.Chars:在当前String对象中获取Char对象的指定位置 |
2.Length:在当前String对象中获取字符数 |
String类的方法
1.public straic int Compare(string strA,string strB | 比较两个指定的string对象,并返回一个表示它们在排列顺序中相对位置的整数该方法区分大小写 |
2.public static int Compare(string strA,string strB,bool ignoreCase) | 比较两个指定的string对象,并返回一个表示它们在排列顺序中相对未知的饿整数。但是,如果布尔参数为真时,该方法不区分大小写 |
3.public static string Concat(string str0,string str1) | 连接两个string对象 |
4.public static string Concat(string str0,string str1,string str2) | 连接三个string对象 |
5.public bool Contains (string value) | 返回一个表示指定string对象是否出现在字符串中的值 |
6.public static string Copy(string str) | 创建一个与指定字符串具有相同值得新的String对象 |
7.public void CopyTo(int sourcelndex,char[] destination,int destinationlndex,int count) | 从string对象的指定位置开始复制指定数量的字符到Unicode字符数组中的指定位置 |
8.public bool EndaWith(string value) | 判断string对象的结尾是否匹配指定的字符串 |
9.public bool Equals(string value) | 判断当前的string对象是否与指定的string对象具有相同的值 |
10.public static bool Equals(string a,string b) | 判断两个指定的string对象是否具有相同的值 |
11.public static string Format(string format,Object arg0) | 吧指定字符串中一个或多个格式项替换为指定对象的字符串表示形式 |
12.public int IndexOf(char value) | 返回指定Unicode字符在当前字符串第一次出现的索引,索引从0开始 |
13.public int lndexOf(string value) | 返回指定字符串在该实例中第一次出现的索引,索引从0开始 |
14.public int IndexOf(char value,int startIndex) | 返回指定Unicode字符从该字符串中指定字符位置开始搜索第一次出现的索引,索引从0开始 |
15.public int IndexOf(string value,int startIndex) | 返回指定字符串从该实例中指定字符位置开始搜索第一次出现的索引,索引从0开始 |
16.public int INdexOfAny(char[] anyOf) | 返回某一个指定的Unicode字符数组中任意字符在该实例中第一次出现的索引,索引从0开始 |
17.public int IndexOfAny(char[] anyOf,int startIndex) | 返回某一个指定的Unicode字符数组中任意字符从该实例中指定字符位置开始搜索第一次出现的索引,索引从0开始 |
18.public string Insert(int startindex,string Value) | 返回一个新的字符串,其中,指定的字符串被插入在当前string对象的指定索引位置 |
19.public static bool IsNullOrEmpty(string value) | 指示指定的字符串是否为null或者是否为一个空的字符串 |
20.public static string Join(string separator,string[] value,int startIndex,int count) | 连接一个字符串数组中的所有元素,使用指定的分隔符分隔每个元素 |
21.public static string Join(string separator, string[] value,int startIndex,int count) | 连接一个字符串数组中的指定位置开始的指定元素,使用指定的分隔符分隔每个元素 |
22.public int LastIndexOf(char value) | 返回指定Unicode字符在当前string对象中最后一次出现的索引位置,索引从0开始 |
23.public int LastIndexOf(string value) | 返回指定字符串在当前string对象中最后一次出现的索引位置,索引从0开始 |
24.public string remove(int startIndex) | 移除当前实例中的所有字符,从指定位置开始,一直到最后一个位置为止,并返回字符串 |
25.public string Remove(int startIndex,int count) | 从当前字符串的指定位置开始移除指定数量的字符,并返回字符串 |
26.public string Replace(char oldChar,char newChar) | 把当前string对象中,所有指定的Unicode字符替换为另一个指定的Unicode字符,并返回新的字符串 |
27.public string Replace(string oldValue,string newValue) | 把当前string对象中,所有指定的字符串替换为另一个指定的字符串,并返回新的字符串 |
28.public string[] Split(params char[] separator) | 返回一个字符串数组,包含当前的string对象中的子字符串,子字符串是使用指定的Unicode字符数组中的元素进行分隔的 |
29.public string[] Split(char[] separator,int count) | 返回一个字符串数组,包含当前的string对象中的子字符串,子字符串是使用指定的Unicode字符数组的元素进行分隔的。int参数指定要返回的子字符串的最大数目 |
30.public bool StartsWith(string value) | 判断字符串实例的开头是否匹配指定的字符串 |
31.public char[] ToCharArray() | 返回一个带有当前string对象中所有字符的Unicode字符数组 |
32.public char[] TocharArray(int startIndex,int length) | 返回一个带有当前string对象中所有字符的Unicode字符数组,从指定的索引开始,直到指定的长度为止 |
33.public string ToLower() | 把字符串转换为小写并返回 |
34.public string ToLower() | 把字符串转换为大写并返回 |
35.public string Trim() | 移除当前String对象中的所有前导空白字符和后置空白字符 |
比较字符串
using System;
namespace zifuchuang
{
class Program
{
static void Main(string[] args)
{
string str1 = "This is test";
string str2 = "This is text";
if(String.Compare(str1,str2)==0)
{
Console.WriteLine(str1 + "and" + str2 + "are equal.");
}
else
{
Console.WriteLine(str1 + "and" + str2 + "are not equal.");
}
Console.ReadKey();
}
}
}
字符串包含字符串
string str3 = "This is test";
if(str3.Contains("test"))
{
Console.WriteLine("The sequence 'test'was found.");
}
Console.ReadKey();
获得子字符串:
string str4 = "Last night I of San Pedro";
Console.WriteLine(str4);
string substr = str4.Substring(23);//从23这字符开始截断
Console.WriteLine(substr);
连接字符串
string[] starray = new string[] {"Down the way nights are dark",
"And the sun shines daily on the mountain top",
"I took a trip on a sailing ship",
"And when i reached Jamaic",
"I made a stop"};
string str = String.Join("\n", starray);
Console.WriteLine(str);
Console.ReadKey();
C#string.Format格式化日期
#region string.Format格式化日期
DateTime dt = new DateTime(2017, 4, 1, 13, 16, 32, 108);
string.Format("{0:y yy yyy yyyy}", dt); //17 17 2017 2017
string.Format("{0:M MM MMM MMMM}", dt);//4 04 四月 四月
string.Format("{0:d dd ddd dddd}", dt);//1 01 周六 星期六
string.Format("{0:t tt}", dt);//下 下午
string.Format("{0:H HH}", dt);//13 13
string.Format("{0:h hh}", dt);//1 01
string.Format("{0:m mm}", dt);//16 16
string.Format("{0:s ss}", dt);//32 32
string.Format("{0:F FF FFF FFFF FFFFF FFFFFF FFFFFFF}", dt);//1 1 108 108 108 108 108
string.Format("{0:f ff fff ffff fffff ffffff fffffff}", dt);//1 10 108 1080 10800 108000 1080000
string.Format("{0:z zz zzz}", dt);//+8 +08 +08:00
string.Format("{0:yyyy/MM/dd HH:mm:ss.fff}", dt); //2017/04/01 13:16:32.108
string.Format("{0:yyyy/MM/dd dddd}", dt); //2017/04/01 星期六
string.Format("{0:yyyy/MM/dd dddd tt hh:mm}", dt); //2017/04/01 星期六 下午 01:16
string.Format("{0:yyyyMMdd}", dt); //20170401
string.Format("{0:yyyy-MM-dd HH:mm:ss.fff}", dt); //2017-04-01 13:16:32.108
#endregion
#region Tostring()实现相同得效果
DateTime dt = new DateTime(2017, 4, 1, 13, 16, 32, 108);
dt.ToString("y yy yyy yyyy");//17 17 2017 2017
dt.ToString("M MM MMM MMMM");//4 04 四月 四月
dt.ToString("d dd ddd dddd");//1 01 周六 星期六
dt.ToString("t tt");//下 下午
dt.ToString("H HH");//13 13
dt.ToString("h hh");//1 01
dt.ToString("m mm");//16 16
dt.ToString("s ss");//32 32
dt.ToString("F FF FFF FFFF FFFFF FFFFFF FFFFFFF");//1 1 108 108 108 108 108
dt.ToString("f ff fff ffff fffff ffffff fffffff");//1 10 108 1080 10800 108000 1080000
dt.ToString("z zz zzz");//+8 +08 +08:00
dt.ToString("yyyy/MM/dd HH:mm:ss.fff"); //2017/04/01 13:16:32.108
dt.ToString("yyyy/MM/dd dddd"); //2017/04/01 星期六
dt.ToString("yyyy/MM/dd dddd tt hh:mm"); //2017/04/01 星期六 下午 01:16
dt.ToString("yyyyMMdd"); //20170401
dt.ToString("yyyy-MM-dd HH:mm:ss.fff"); //2017-04-01 13:16:32.108
#endregion
C#结构体
结构体是值类型数据结构,它使得一个单一个变量可以存储各种数据类型的相关数据。struct关键字用于创建结构体。结构体是用来代表一个记录,假设想跟踪图书馆种书的动态,可能想跟踪每本书的以下属性:Title、Author、Subject、Book ID
定义结构体:为了定义一个结构体,必须使用struct语句。struct语句为程序定义了一个带有多个成员的新的数据类型。可以按照以下的方式声明Book结构
struct Books//不在主程序内
{
public string title;
public string author;
public string subject;
public int book_id;
};
using System;
namespace _11_30jiegou
{
class Program
{
struct Books//不在主程序内
{
public string title;
public string author;
public string subject;
public int book_id;
};
static void Main(string[] args)
{
Books Book1;//声明Book1,类型为Books
Books Book2;//声明Book2,类型为Books
//Book1 详述
Book1.title = "生";
Book1.author = "颜艺";
Book1.subject = "意义";
Book1.book_id = 61001;
//Book2 详述
Book2.title = "生";
Book2.author = "颜艺";
Book2.subject = "意义";
Book2.book_id = 61002;
//打印Book1信息
Console.WriteLine("标题 :{0}", Book1.title);
Console.WriteLine("作者 :{0}", Book1.author);
Console.WriteLine("话题 :{0}", Book1.subject);
Console.WriteLine("书号 :{0}", Book1.book_id);
//打印Book2信息
Console.WriteLine("标题 :{0}", Book2.title);
Console.WriteLine("作者 :{0}", Book2.author);
Console.WriteLine("话题 :{0}", Book2.subject);
Console.WriteLine("书号 :{0}", Book2.book_id);
Console.ReadKey();
#region 输出结果
//标题: 生
//作者 :颜艺
//话题 :意义
//书号 :61001
//标题: 生
//作者 :颜艺
//话题 :意义
//书号 :61002
#endregion
}
}
}
结构的特点:
已经用一个简单的名为Books的结构。在C#中的结构与传统的C或C++中的结构不同。C#中的结构有以下特点:1.结构可带有方法、字段、索引、属性、运算符方法和事件
2.结构可定义构造函数,但不能定义析构函数。但是,不能为结构定义无参构造函数(默认)是自动定义的,且不能被改变的。
3.与类不同,结构不能继承其他的结构或类
4.结构不能作为其他结构或类的的基础结构
5.结构可实现一个或多个接口
6.结构成员不能指定为absteact、virtual或protected
7.当使用New操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不适用New操作符即可被实例化
8.如果不使用New操作符,只有在所有的字段都被初始化之后,字段才被赋值,对象才被使用
类VS结构
类和结构有以下几个脚本的不同点:
1.类是引用类型,结构是值类型
2.结构不支持继承
3.结构不能声明默认的构造函数
using System;
namespace _11_30jiegou
{
struct Books//不在主程序内
{
public string title;
public string author;
public string subject;
public 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);
}
};
class Program
{
static void Main(string[] args)
{
Books Book1 = new Books();//声明Book1,类型为Books
Books Book2 = new Books();//声明Book2,类型为Books
//Book1 详述1
Book1.title = "生";
Book1.author = "颜艺";
Book1.subject = "意义";
Book1.book_id = 61001;
//Book1 详述2
Book1.setValues("c语言", "颜", "易", 61003);
//Book2 详述1
Book2.title = "生";
Book2.author = "颜艺";
Book2.subject = "意义";
Book2.book_id = 61002;
//Book2 详述2
Book2.setValues("c语言", "颜", "易", 61003);
//打印Book1信息1
Console.WriteLine("标题 :{0}", Book1.title);
Console.WriteLine("作者 :{0}", Book1.author);
Console.WriteLine("话题 :{0}", Book1.subject);
Console.WriteLine("书号 :{0}", Book1.book_id);
//打印Book1信息2
Book1.display();
//打印Book2信息1
Console.WriteLine("标题 :{0}", Book2.title);
Console.WriteLine("作者 :{0}", Book2.author);
Console.WriteLine("话题 :{0}", Book2.subject);
Console.WriteLine("书号 :{0}", Book2.book_id);
//打印Book2信息2
Book1.display();
Console.ReadKey();
#region 输出结果
//标题: c语言
//作者 :颜
//话题 :易
//书号 :61003
//Title: c语言
// Author:颜
// Subject:易
// Book_id:61003
//标题: c语言
//作者 :颜
//话题 :易
//书号 :61003
//Title: c语言
// Author:颜
// Subject:易
// Book_id:61003
#endregion
}
}
}
结构体中声明的字段无法赋值初值,类可以
struct test001
{
private int aa =1;
}
会出现错误,“结构中不能实例属性或字段初始值设定”,而类没有限制
class test002
{
private int aa=1;
}
结构体的构造函数中,必须为结构体所有字段赋值,类的构造函数无此限制
类与结构的选择
首先明确,类的对象是存储在堆空间中,结构存储在栈中。堆空间大,但访问速度较慢,栈空间小,访问速度相对更快。故而,当我们描述一个轻量级对象的时候,结构可提高效率,成本更低。当然,这也得从需求出发,假如我们在传值的时候希望传递的是对象的引用地址而不是对象的拷贝,就应该使用类了。
结构和类的适用场合分析:
1.当堆栈的空间很有限,且有大量的逻辑对象时,创建类要比创建结构好一些;
2.对于点、矩形和颜色这样的轻量对象,假如要声明一个含有许多个颜色对象的数组,则CLR需要为每个对象分配内存,在这种情况下,使用结构的成本较低;
3.在表现抽象和多级别的对象层次时,类是最好的选择,因为结构不支持继承。
4.大多数情况下,目标类型只是含有一些数据,或者以数据为主。
C#枚举(Enum)
枚举是一组命名整型常量,枚举类型是使用enum关键字声明
C#枚举是值类型,换句话说,枚举包含自己的值,且不能继承或传递继承
声明枚举的一般语法:
enum<enum_name>
{
enumeration list
};
enum_name指定枚举的类型名称
enumeration 是一个用逗号分隔的标识符的列表
枚举元素默认的值的类型是 int 型,可以修改为其他整型 byte、sbyte、short、ushort、int、uint、long 和 ulong。
枚举列表中的每个符号代表一个整数值,一个比它前面的符号大的整数值。默认情况下,,第一个枚举符号的值是0
enum Days{ Sun,Mon,tue,Wed,thu,Fri,Sat};
默认情况下第一个枚举符号的值是0。但也可以设定每个符号的值,如果字符e没有设定值,那他的值就是字符(n-1)+1.比如枚举字符g,他的值就是f的值+1,但f的值夜不知道,就会去找第(n-1)的值,也就是e的值,以此类推。找到的c的值1,e=c+1=2,f=e+1=3,g=f+1=4;
using System;
class Program
{
enum Day { a = 8, b, c = 1, e, f, g };
static void Main(string[] args)
{
int a = (int)Day.a;
int b = (int)Day.e;
Console.WriteLine(a);
Console.WriteLine(b);
Console.ReadKey();
}
}
建议:
枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。
说明: 枚举其实就是特殊的常量类,且构造方法被默认强制是私有。
正例: 枚举名字: DealStatusEnum, 成员名称: SUCCESS / UNKOWN_REASON
C#类(Class)
当定义一个类时,就定义了一个数据类型的蓝图。这实际上并没有定义任何的数据,但它定义了类的名称意味着什么,也就是说,类的对象由什么组成及在这个对象上可执行什么操作。对象是类的实例。构成类的方法和变量称为类的成员
类的定义
是以关键字class开始后跟类的名称。类的主体,包含在一对花括号内,下面是类定义的一般形式
//access soecifuer 是指定对类及其成员的访问规则
//如果没有指定,则使用默认的访问标识符
//类的默认访问标识符是internal,成员的默认访问标识符是private
<access specifier> class class_name
{
//数据类型
//数据类型<data type>指定了变量的类型,返回类型<return type>指定返回的数据类型
//如果要访问类的成员,需要使用(.)运算符
//点运算符链接了对象的名称和成员的名称
<access specifier> <data type> variable1;
<access specifier> <data type> variable2;
...
<access specifier> <data type> variableN;
<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
}
}
实例说明目前为止所概念:
using System;
namespace _12_21Lei
{
class Box
{
public double length;//长度
public double breadth;//宽度
public double height;//高度
}
class Program
{
static void Main(string[] args)
{
Box Box1 = new Box();//声明Box1 类型为Box
Box Box2 = new Box();//声明Box2 类型类Box
double volume = 0.0;//体积
//Box1 详述
Box1.height = 5.0;
Box1.length = 6.0;
Box1.breadth = 7.0;
//Box2 详述
Box2.height = 10.0;
Box2.length = 11.0;
Box2.breadth = 12.0;
//Box1 的体积
volume = Box1.height * Box1.length * Box1.breadth;
Console.WriteLine("Box1的体积:{0}", volume);
//Box2 的体积
volume = Box2.height * Box2.length * Box2.breadth;
Console.WriteLine("Box2的体积:{0}", volume);
Console.ReadKey();
}
}
}
上面实现封装之后,保持私有来实现封装。只能公共成员函数来访问
using System;
namespace _12_21Lei
{
class Box
{
//对象(类)本身在对象内部可以访问
private double length;//长度
private double breadth;//宽度
private double height;//高度
public void Setlength(double len)
//执行这个函数需要传一个double类型的参数进去
{
length = len;
}
public void Setbreadth(double bre)
{
breadth = bre;
}
public void Setheight(double hei)
{
height = hei;
}
public double Getvolume()
{
return length * breadth * height;
}
}
class Program
{
static void Main(string[] args)
{
Box Box1 = new Box();//声明Box1 类型为Box
Box Box2 = new Box();//声明Box2 类型类Box
double volume;
//Box1详述
Box1.Setlength(5.0);
Box1.Setbreadth(6.0);
Box1.Setheight(7.0);
volume = Box1.Getvolume();
Console.WriteLine("Box1的体积:{0}", volume);
//Box2详述
Box2.Setlength(10.0);
Box2.Setbreadth(11.0);
Box2.Setheight(12.0);
volume = Box2.Getvolume();
Console.WriteLine("Box2的体积:{0}", volume);
Console.ReadKey();
}
}
}
C#中的构造函数
类的构造函数是类的一个特殊的成员函数,当创建类的新对象时执行
构造函数的名称与类的名称完全相同,它没有如何返回类型
实例说明了构造函数的概念
using System;
namespace _12_21GouZao
{
class Line
{
private double length;//线条长度
public Line()
{
Console.WriteLine("对象已创建");
}
public void setLength(double len)
{
length = len;
}
public double getLength()
{
return length;
}
}
class Program
{
static void Main(string[] args)
{
Line line = new Line();
line.setLength(6.0);
Console.WriteLine("线条的长度:{0}", line.getLength());
Console.ReadKey();
}
}
}
默认的构造函数没有任何参数,如果需要一个带有参数的构造函数可以又参数。这种构造函数叫参数化构造函数. 创建对象的同时给对象赋初始值,具体看下例:
using System;
namespace _12_21GouZao
{
class Line
{
private double length;//线条长度
public Line(double len)
//执行这个函数你要传一个double类型的参数进去
{
Console.WriteLine("对象已创建,length={0}",len);
length = len;
}
public void setLength(double len)
{
length = len;
}
public double getLength()
{
return length;
}
}
class Program
{
static void Main(string[] args)
{
Line line = new Line(10.0);
Console.WriteLine("线条的长度:{0}", line.getLength());
line.setLength(6.0);
Console.WriteLine("线条的长度:{0}", line.getLength());
Console.ReadKey();
}
}
}
C#中的析构函数
类的析构函数是类的一个特殊的成员函数,当类的对象超出范围时执行
析构函数的名称是在类的名称前加上一个波浪形(~)作为前缀,它不返回值,也不带任何参数
析构函数用于在结束程序(比如关闭文件,释放内存等)之前释放资源。析构函数不能继承或重载
实例说明析构函数的概念:
using System;
namespace _12_21XiGou
{
class Line
{
private double length;
public Line() //构造函数
{
Console.WriteLine("对象已创建");
}
~Line()//析构函数
{
Console.WriteLine("对象已删除");
}
public void setLength(double len)
{
length = len;
}
public double getLength()
{
return length;
}
}
class Program
{
static void Main(string[] args)
{
Line line = new Line ();
line.setLength(6.0);
Console.WriteLine("线条的长度:{0}", line.getLength());
}
}
}
C#类的静态成员
可以使用static关键字把类成员定义为静态的,当声明一个类成员为静态时,意味着无论有多少个类的对象被创建,只会有一个该静态成员的副本。
关键字static意味着类中只有一个该成员的实例。静态变量用于定义常量,因为它们的值可以通过直接调用类而不需要创建类的实例来获取。静态变量可在成员函数或类的定义外部进行初始化。也可以在类的定义内部初始化静态变量
实例演示静态变量的用法:
namespace Tools
{
public class tools
{
public static void hello(string a_) //强制要求标注静态和非静态
{
System.Console.WriteLine(a_);
}
}
}
namespace ConsoleApp2
{
class A
{
static void Main()
{
Tools.tools.hello("hello"); // 这个时候整个类就是静态,不需要实例化
}
}
}
//假如因为某种需要,需要变成非静态,也就是动态化的需要,那么就需要实例化
namespace Tools
{
public class tools
{
public void init()
{
WriteLine("实例化");
}
public static void hello(string a_)
{
System.Console.WriteLine(a_);
}
}
}
namespace ConsoleApp2
{
class A
{
static void Main()
{
Tools.tools a = new Tools.tools(); // 如果不实例化,绝对无法调用
a.init();
Tools.tools.hello("hello"); // 但是不影响直接调用静态方法
}
}
}
//什么时候才需要去使用动态?不是只需要经过一个工具加工,需要在几个工具之间传递数据然后去处理
//因为静态方法之间是无法传递数据的,这个时候就需要用到动态:
namespace Tools
{
public class tools
{
int a, b; // 储存在类的内部
public void init(int a, int b) // 用于接收数值
{
this.a = a;
this.b = b;
WriteLine("初始化构造");
}
public int rectangle() // 用于计算数值和传递
{
return this.a * this.b;
}
}
}
namespace ConsoleApp2
{
class A
{
static void Main()
{
Tools.tools a = new Tools.tools();
a.init(1, 2);
}
}
}
注 大佬解释:
其实可以把类比成一个坐不同口味糖的机器,作出了原型机的设计图之后,我需要用不同的原料来制造我需要的口味的糖,这个时候我就需要制造出一个专门用于生产比如草莓口味的糖,这个就是实例化,但是每个机器肯定都是独立的,因为不能让她们串味,所以每个机器都需要单独创立。理解这点之后,在去理解里面的构造函数之类的概念,就可以理解了,万变不离其宗。
C#继承
继承是面向对象程序设计中最重要的概念之一,继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易。同时也有利于重用代码和节省开发时间
当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可。这个已有的类被称为基类,这个新的类被称为派生类
继承的思想实现了属于(IS-A)关系。例如,哺乳动物属于(IS-A)动物,狗属于(IS-A)哺乳动物,因此狗属于(IS-A)动物
基类和派生类
一个类可以派生自多个类或接口,这意味着它可以从多个基类或接口继承数据和函数
C#中创建派生类的语法如下:
<访问修饰符> class <基类>
{
...
}
class <派生类> : <基类>
{
...
}
假设有个基类Shape,它的派生类是Rectangle:
using System;
namespace _12_21JingTai
{
class Shape//基类
{
protected int width;
protected int height;
public void setWidth(int w)
{
width = w;
}
public void setHeight(int h)
{
height = h;
}
}
//派生类
class Rectangle:Shape
{
public int GetArea()
{
return (width * height);
}
}
class Program
{
static void Main(string[] args)
{
Rectangle Rect = new Rectangle();
Rect.setWidth(5);
Rect.setHeight(7);
//打印对象的面积
Console.WriteLine("总面积:{0}", Rect.GetArea());
Console.ReadKey();
}
}
}
基类的初始化
派生类继承了基类的成员变量和成员方法。因此父类对象应在子类对象创建之前被创建。可以在成员初始化列表中进行父类的初始化