赞
踩
本文主要供自身复习使用,主要内容就是将C#基础知识进行大纲式针对性的梳理。
C# 是由微软(Microsoft)开发的一个简单的、现代的、通用的、面向对象的编程语言。
我们常见的.Net 框架的组件:
- 公共语言运行库(Common Language Runtime - CLR)
- .Net 框架类库(.Net Framework Class Library)
- 公共语言规范(Common Language Specification)
- 通用类型系统(Common Type System)
- 元数据(Metadata)和组件(Assemblies)
- Windows 窗体(Windows Forms)
- ASP.Net 和 ASP.Net AJAX
- ADO.Net
- Windows 工作流基础(Windows Workflow Foundation - WF)
- Windows 显示基础(Windows Presentation Foundation)
- Windows 通信基础(Windows Communication Foundation - WCF)
- LINQ
2 .Net Core
.Net Core是微软继.Net Framework之后推出的可以跨平台的平台,可以用来创建运行在mac、Linux上的应用程序
3 .Net
完成了对.Net Framework和.Net Core的整合,是一个全新的强大的跨平台的平台
.net就像是大舞台,C#就是其中的一个演员,演员可以使用平台提供的话筒(wpf),用来唱歌(创建应用程序);也可以使用平台提供的舞池(web)跳舞(创建web应用),C#只可以在这个大舞台上展示,但是这个舞台除了可以提供C#这个演员展示,还可以提供C++、Visual Basic、Jscript、COBOL 等演员展示
目前主要使用的是Visual Studio 2022 (VS)
控制台应用程序是C# 入门时,学习基础语法的最佳应用程序。
控制台应用程序是入门级的应用程序,一般常用的代码如下:
//输出 Console.Write(); //换行输出 Console.WriteLine(); //以上代码主要用于在控制台输出信息 //读取输入 Console.Read(); //读取当前行输入 Console.ReadLine(); //读取字符 Console.ReadKey(); //以上代码主要用于在控制读取输入的信息 //接受用户输入的字符串,以换行结束 string s=Console.ReadLine();
先看如下示例代码:
class Rectangle { // 成员变量 double length; double width; public double GetArea() { length = 3; width = 2; return length * width; } public void Display() { Console.WriteLine($"Length: {length}"); Console.WriteLine($"Width: {width}"); Console.WriteLine($"Area: {GetArea()}"); } }
标识符命名规则:
- 标识符必须以字母、下划线或 @ 开头,后面可以跟一系列的字母、数字( 0 - 9 )、下划线( _ )、@。
- 标识符中的第一个字符不能是数字。
- 标识符必须不包含任何嵌入的空格或符号,比如 ? - +! # % ^ & * ( ) [ ] { } . ; : " ’ / \。
- 标识符不能是 C# 关键字。除非它们有一个 @ 前缀。 例如,@if 是有效的标识符,但 if 不是,因为 if 是关键字。
- 标识符必须区分大小写。大写字母和小写字母被认为是不同的字母。
- 不能与C#的类库名称相同。
如上面的length、width、GetArea、Display 等变量或函数的名称都是标识符
首先我们写一个示例代码:
class UserInfo { private string _id; private string _name; public string Id { get { return _id; } set { _id = value; } } public string Name { get { return _name; } set { _name = value; } } }
上面的代码中:_id,_name 是字段,Id,Name 是属性,而_id,_name,Id,Name都是成员变量。
在C#中,有以下几种类型:
值类型变量可以直接分配给一个值。它们是从类 System.ValueType 中派生的。值类型直接包含数据。
值类型又可分为:
分类 | 详情 | 备注 |
---|---|---|
整型 | byte short ushort int uint long ulong | |
浮点型 | float double decimal | decimal多用于金钱计算上 |
字符型 | char | 每个字符背后都对应着一个ascii码,“A”为65;“a”为97;“0”为 48 |
布尔型 | bool | true/false |
可以为空的值类型 | 如 int? | 值为 null 的其他所有值类型的扩展 |
结构体 | struct | |
枚举 | enum | |
元组值类型 | 形如 (double,int)t1=(1.1,2); | 详见C#元组的使用 |
引用类型不包含存储在变量中的实际数据,但它们包含对变量的引用。换句话说,它们指的是一个内存位置。使用多个变量时,引用类型可以指向一个内存位置。如果内存位置的数据是由一个变量改变的,其他变量会自动反映这种值的变化。内置的 引用类型有:object、dynamic 和 string。
分类 | 详情 | 备注 |
---|---|---|
对象类型Object | 对象(Object)类型 是 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类 | 数组,集合,用户定义的类、接口、委托,object, |
动态类型Dynamic | 您可以存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。 | 动态类型与对象类型相似,但是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。 |
字符串类型String | 字符串(String)类型 允许您给变量分配任何字符串值 | 这里需要注意的就是转义符的使用 |
\
的使用与扩展运用 | 详情 | 备注 |
---|---|---|
\ | 输出特殊字符 | 使用在字符串中,使用\则可以输出“”和\ 等字符 |
\n | 换行 | 使用在字符串中,输出的时候会自动换行 |
\t | 制表符 | 使用在字符串中,输出的时候会自动对齐,隔开 |
\b | 去掉上一个字符串 | 如果需要输出\b则需进行转义 |
string path1 = "E:\\MyCode\\my-study";
string path2 = @"E:\MyCode\my-study";
Console.WriteLine($"path1:{path1}-------path2:{path2}");
由上可知,当前我们使用@进行字符串转义的话,我们可以避免使用“\”逐个的进行转移。
指针类型变量存储另一种类型的内存地址。C# 中的指针与 C 或 C++ 中的指针有相同的功能。
声明指针类型的语法:type* identifier;
如:char* cptr; int* iptr;
类型转换就是将数据从一种类型转换为另一种类型,类型转换有两种形式:
这些转换是 C# 默认的以安全方式进行的转换, 不会导致数据丢失。将类型较小的数据存放到较大的变量中
显式转换需要强制转换运算符,而且强制转换会造成数据丢失。将类型较大的数据存放在较小的变量中
案例代码如下:
static void Main(string[] args) { int num1 = 1; long num2 = num1;//隐式类型转换 //强制类型转换:(数据类型)数据 double num3 = 3.56; int num4 = (int)num3; int num5 = Convert.ToInt32(num3); //Convert 对象转换 Console.WriteLine($"3.56强制转换后的结果:{num4}\n3.56使用Convert对象转换的结果:{num5}"); //3.56强制转换后的结果:3 //3.56使用Convert对象转换的结果:4 Console.ReadLine(); }
由上面的代码案例可以清晰的了解数据的转换,另外还可从上面的案例可知,显式转换存在两种形式,一种是强制类型转换,这种方式对于浮点型不会进行四舍五入,直接将小数点后面的数据舍弃;另一种是使用Convert对象进行转换,这种方式则会对浮点型数据进行四舍五入的计算,然后进行转换。
- 装箱是将值类型转换为引用类型; 拆箱就是将引用类型转换为值类型
示例代码如下:
static void Main(string[] args)
{
//装箱:值类型转换为引用类型
int num = 111;
object obj = num;
Console.WriteLine($"obj = {obj}"); //obj = 111
//拆箱:引用类型转换为值类型
int nnum = 222;
object oobj=nnum;
int result = (int)oobj;
Console.WriteLine($"result = {result}");//result = 222
//【注意】:被装过箱的对象才能被拆箱
}
我们知道平常写代码的时候要尽量的避免装箱和拆箱,但是为什么要这样呢?要搞清白这些啊,那就必须知道,什么是堆和栈以及装箱和拆箱的过程
堆栈简单来说,就是计算机存储数据的一个数据模型。
模型分为堆和栈两部分,栈中主要存放一些简单结构的数据和复杂数据的索引,堆中存储复杂数据结构的数据内容。简单的数据结构就是值类型,复杂数据结构就是引用类型
装箱过程:
①先在堆上为新生成的引用对象分配内存,
②然后将值类型的数据copy到刚分配的内存中,
③最后返回内存的地址(引用对象在栈中会存储这个内存的地址,相当于数据的索引)。
在装箱的过程中分配内存和数据复制都是消耗效能的操作
拆箱过程:
①先检查当前的引用类型数据是否可以转换为目标值类型数据
(例如,string str="123"可以转换为Int ,而str="asd"不可以)
②复制堆中的数据的值并将复制的值放到目标数据所在的存储位置
拆箱过程有类型检查和复制数据两个操作,其中复制数据耗费性能
由此我们可知,装箱和拆箱比较影响程序运行的性能,所以我们需要尽量的避免此类操作!
常量是固定值,程序执行期间不会改变。常量可以是任何基本数据类型,比如整数常量、浮点常量、字符常量或者字符串常量,还有枚举常量。
常量可以被当作常规的变量,只是它们的值在定义后不能被修改。
详情可见:C#常量,这里重点说一下定义常量,常量是使用const关键字来定义的,示例如下:
public const string URL = "www.xxxx.com";
public const int num = 999;
常量扩展:
静态常量(编译时常量)const 和动态常量(运行时常量)readonly
静态常量在编译时就确定了值,必须在声明时就进行初始化且之后不能进行更改,可在类和方法中定义。定义方法如下:
const double a=3.14;// 正确声明常量的方法
const int b; // 错误,没有初始化
动态常量在运行时确定值,只能在声明时或构造函数中初始化,只能在类中定义。定义方法如下:
class Program
{
readonly int a=1; // 声明时初始化
readonly int b; // 构造函数中初始化
Program()
{
b=2;
}
static void Main()
{
}
}
运算符是一种告诉编译器执行特定的数学或逻辑操作的符号。C# 有丰富的内置运算符,分类如下:
运算符 | 描述 | 备注 |
---|---|---|
+ | 将两个操作数相加 | 当字符串和数值类使用 + 则是拼接字符串 |
- | 将两个操作数相减 | |
* | 将两个操作数相乘 | |
/ | 将两个操作数相除 | 当两个操作数均为整数的时候,结果取商; 当两边有一个为浮点型的时候,做精确运算 |
% | 模运算符,整除后取余数 | 如 21%10=1 |
++ | 自增运算符,整数值增加1 | 注意:当自增运算符在前,则先自增,然后再使用自增后的赋值; 当自增运算符在后,则先赋值,然后再自增 |
- - | 自减运算符,整数值减去1 | 同上 |
注意:在char类型参与到算数运算时,会自动转化为ascii码10进制的值参与运算
class Program { static void Main(string[] args) { int a = 1; int b; // a++ 先赋值再进行自增运算 b = a++; Console.WriteLine("a = {0}", a); Console.WriteLine("b = {0}", b); Console.ReadLine();//结果a=2,b=1 // ++a 先进行自增运算再赋值 a = 1; // 重新初始化 a b = ++a; Console.WriteLine("a = {0}", a); Console.WriteLine("b = {0}", b); Console.ReadLine();//结果a=2,b=2 // a-- 先赋值再进行自减运算 a = 1; // 重新初始化 a b= a--; Console.WriteLine("a = {0}", a); Console.WriteLine("b = {0}", b); Console.ReadLine();//结果a=0,b=1 // --a 先进行自减运算再赋值 a = 1; // 重新初始化 a b= --a; Console.WriteLine("a = {0}", a); Console.WriteLine("b = {0}", b); Console.ReadLine();//结果a=0,b=0 } }
运算符 | 描述 |
---|---|
== | 检查两个操作数的值是否相等,如果相等则条件为真 |
!= | 检查两个操作数的值是否相等,如果不相等则条件为真。 |
> | 检查左操作数的值是否大于右操作数的值,如果是则条件为真 |
< | 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 |
>= | 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 |
<= | 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。 |
注:关系运算符的运算结果均是bool ,常运用于判断语句中
运算符 | 描述 |
---|---|
|| | 或 运算符,当两个操作数均为false ,则结果为false,其余均为true |
&& | 与 运算符, 当两个操作数均为true,则结果为true,其余均为false |
! | 非 运算符,取反 |
注:
① 逻辑运算符的短路效果,当运算符的左边的结果对整个表达式结果起了决定性的作用的时候,运算符右边的表达式就不会运行。
如:当&&运算符左边为false的时候,此时无论运算符右边是什么,结果都为false,那么右边表达式就不会运行。
② 当单独使用| 和& 就不会产生短路的效果,即使左边已经起了决定性作用,右边仍会运行。
运算符 | 描述 |
---|---|
& | 与 位运算符,当两个操作数均为1 ,则结果为1,其余均为0 |
| | 或 位运算符, 当两个操作数均为0,则结果为0,其余均为1 |
^ | 异或 运算符,当两个操作均为0或1,则结果为0,其余结果为1 |
~ | 按位取反 运算符,具有翻转位的效果,可将0变1,1变0 |
<< | 二进制左移运算符。左操作数的值向左移动右操作数的位数 |
>> | 二进制右移运算符。左操作数的值向右移动右操作数指定的位数。 |
辅助理解的案例代码如下:
static void Main(string[] args) { //60 和13 转换位二进制分别是111100 和 1101 ,不足8位【前面】使用0补足 int a = 60; /* 60 = 0011 1100 */ int b = 13; /* 13 = 0000 1101 */ int c = 0; c = a & b; // a 0011 1100 // b 0000 1101 //----------------- 根据&运算符规则:只有均为1的时候,结果才为1,否则均为0 // c 0000 1100===>转换为十进制 则是12 Console.WriteLine("Line 1 - c 的值是 {0}", c); c = a | b; // a 0011 1100 // b 0000 1101 //----------------- 根据|运算符规则:只有均为0的时候,结果才为0,否则均为1 // c 0011 1101===>转换为十进制 则是61 Console.WriteLine("Line 2 - c 的值是 {0}", c); c = a ^ b; // a 0011 1100 // b 0000 1101 //----------------- 根据^运算符规则:只有均为0或1的时候,结果才为0,否则均为1 // c 0011 0001===>转换为十进制 则是49 Console.WriteLine("Line 3 - c 的值是 {0}", c); c = ~a; /*-61 = 1100 0011 */ // a 0011 1100 //取反----------------- 根据~运算符规则:将0变1 ,1变0 // c 1100 0011===>转换为十进制 则是-61 Console.WriteLine("Line 4 - c 的值是 {0}", c); c = a << 2; // a 0011 1100 //左移----------------- 0011 1100 向左移动两位则将前面两位00 移掉了,然后后面补足两位00 // c 1111 0000===>转换为十进制 则是240 Console.WriteLine("Line 5 - c 的值是 {0}", c); c = a >> 2; // a 0011 1100 //右移----------------- 0011 1100 向右移动两位则将后面两位00 移掉了,然后前面补足两位00 // c 0000 1111===>转换为十进制 则是15 Console.WriteLine("Line 6 - c 的值是 {0}", c); Console.ReadLine(); }
这里包括有 is ,as ,typeof 等运算符.
public class Base { } public class Derived : Base { } class Program { static void Main(string[] args) { // 基类和派生类 之间的is 类型检查 var b = new Base(); Console.WriteLine(b is Base);//True Console.WriteLine(b is Derived);//False var d = new Derived(); Console.WriteLine(d is Base);//True Console.WriteLine(d is Derived);//True //派生类可以是基类类型,is 检查返回True //基类不可以是派生类的类型; //这个道理就像:兔子,狮子,小狗都可以用动物称呼,但是说所有动物都是小狗那就不行了 //is 运算符将考虑装箱和取消装箱转换,但不会考虑数值转换 int i = 27; object iBoxed = i; Console.WriteLine(iBoxed is int); // output: True Console.WriteLine(iBoxed is long); // output: False object obj = 99.9; if (obj is string s) { Console.WriteLine("结果:"+s); } if (obj is double score) { Console.WriteLine("结果:" + score); } //以上案例说明:is的本质是 检查类型,通过检查返回True,反之,False //检查是否为null if (obj is null) { Console.WriteLine("**********"); } //检查是否为非null if (obj is not null) { Console.WriteLine("**********"); } Console.ReadLine(); } }
形如(int)12
) 不同,as 运算符永远不会引发异常。object obj1 = "121212"; int le1 = (obj1 as string).Length;//可以转换 object obj2 = null; int le2 = (obj2 as string).Length;//可以转换,但是由于转换后值为null,因此运行时会报错 object obj3 = new object(); string sr = obj as string; if (sr == null) { Console.WriteLine("转换失败"); } else { Console.WriteLine("转换成功"); } //使用 as 转换的时候需要注意,转为结果为null的情况 //is 对比 as if (obj3 is string str3) { Console.WriteLine($"转换成功{str3}"); } else { Console.WriteLine($"转换失败"); }
public class Animal { } public class Giraffe : Animal { } public static class TypeOfExample { public static void Main() { object b = new Giraffe(); Console.WriteLine(b is Animal); // output: True Console.WriteLine(b.GetType() == typeof(Animal)); // output: False Console.WriteLine(b is Giraffe); // output: True Console.WriteLine(b.GetType() == typeof(Giraffe)); // output: True } }
辅助理解的案例代码如下:
//算术运算符和赋值运算符的优先级:
int a = 10, b = 5, c = 2;
a += b * c; // 先计算乘法,再执行加法赋值
Console.WriteLine(a); // 输出20
//逻辑运算符的优先级:
bool a = true, b = false, c = true;
bool result = a || b && c; // 先计算与运算,再计算或运算
Console.WriteLine(result); // 输出true
//条件运算符的优先级:
int a = 10, b = 5;
string result = a > b ? "a大于b" : "a不大于b"; // 先判断大小关系,再执行条件语句
Console.WriteLine(result); // 输出"a大于b"
//第一种:只有if,没有else if (num>15) { //... } //第二种:有if else if (num > 15) { //... } else { //... } //第三种:多种判断if else if ...else if (num>15) { //... } else if (num>0) { //... } else { //... }
一个 switch 语句允许测试一个变量等于多个值时的情况,算是if else 在某一层面上的进阶版。
switch 语句中的表达式 必须是一个整型或枚举类型,或者是一个 class 类型,其中 class 有一个单一的转换函数将其转换为整型或枚举类型
运用switch的典型案例:输入年月,得出该月的天数
static void Main(string[] args) { Console.WriteLine("请输入年份:"); int year = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("请输入月份:"); int month = Convert.ToInt32(Console.ReadLine()); int day = 0;//定义天数 switch (month) { //大月 case 1: case 3: case 5: case 7: case 8: case 10: case 12: day = 31; break; //小月 case 4: case 6: case 9: case 11: day = 30; break; case 2: //主要是需要对二月进行判断,是否是闰月 if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) { //满足条件,则为闰年中的闰月,输出29天 day = 29; break; } else { day = 28; break; } default: Console.WriteLine("请输入正确的月份数字!"); break; } Console.WriteLine($"{year}年{month}月有{day}天"); Console.ReadLine(); }
x?y:z 表示如果表达式 x 为 true,则返回 y;如果 x 为 false,则返回 z,是 if{}else{} 的简单形式。
static void Main(string[] args)
{
int a=11;
int b=22;
int c = a > b ? a : b;
Console.WriteLine($"c={c}");
Console.ReadLine();//结果: c=22
}
循环语句允许我们多次执行一个语句或语句组。
简单使用案例:
for (int i = 0; i < 10; i++)
{
Console.WriteLine($"i={i}");
}
Console.ReadLine();
当我们需要无限循环的时候,只需要如下使用:
//将各项条件均省略,程序会默认为true,一直运行
for (; ;)
{
Console.WriteLine("我是无限循环");
}
常见题目:百钱百鸡,兔子生小兔子(斐波拉且数列),猜价格(价格截半法)
static void Main(string[] args) { //某人有100元钱,要买100只鸡。 //公鸡5元钱一只,母鸡3元钱一只,小鸡一元钱3只。 //编程计算可买到公鸡,母鸡,小鸡各为多少只? //首先从循环次数上将,从公鸡开始循环次数最少 //首先100元最多买20只公鸡, for (int i = 0; i < 20; i++)//i为公鸡数 { //j为母鸡数 for (int j = 0; j < 33; j++) { if ((100-i-j)%3==0&&i*5+j*3+(100-i-j)/3==100) { Console.WriteLine($"公鸡={i},母鸡={j},小鸡={100-i-j}"); } } } Console.ReadLine(); }
static void Main(string[] args) { //程序循环猜商品价格 //首先定义价格范围 int max = 1000; int min = 1; //定义随机数 Random rnd = new Random(); //定义一个标记,一会使用goto重新从标记位执行代码,方便测试使用 Flag: //随机一个正确价格 int correctPrice = rnd.Next(min,max+1); Console.WriteLine($"正确价格是:{correctPrice}"); //开始猜价格 for (int i = 1;; i++) { int price = (min + max) / 2; if (correctPrice > price) { Console.WriteLine($"第{i}次猜测的价格:{price},低了"); min = price + 1; } else if (correctPrice < price) { Console.WriteLine($"第{i}次猜测的价格:{price},高了"); max = price - 1; } else { Console.WriteLine($"恭喜你,第{i}次猜测的价格:{price},正确"); break; } } if (Console.ReadKey().KeyChar=='A') { min = 1; max = 1000; goto Flag; } Console.ReadLine(); }
注意:理解案例中的for循环的运用以及价格截半的精髓(二分查找),另外案例中有个goto关键字的运行和使用
使用foreach可以迭代数组或者一个集合对象。
static void Main(string[] args)
{
string[] arrStr = new string[] { "ab", "cd", "ef" };
foreach (var item in arrStr)
{
Console.WriteLine($"遍历数组-当前项{item}");
}
Console.ReadLine();
}
foreach 迭代遍历只能是正向的,不可逆的,逐个的遍历数组中的每一个元素;迭代遍历时,被迭代的数组的元素不能更改;迭代遍历的速度远快于for循环。
简单使用案例:
static void Main(string[] args)
{
/* 局部变量定义 */
int a = 10;
/* while 循环执行 */
while (a < 20)//只要条件为true,也可无限循环下去
{
Console.WriteLine("a 的值: {0}", a);
a++;
}
Console.ReadLine();
}
static void Main(string[] args)
{
/* 局部变量定义 */
int a = 10;
/* do 循环执行 */
do
{
Console.WriteLine("a 的值: {0}", a);
a++;
} while (a < 20);
Console.ReadLine();
}
先执行do,然后判断while的条件,当条件为true的时候才进入循环;相较于while循环,do…while无论是否满足循环条件,都会先执行一次业务代码。
循环控制语句更改执行的正常序列。当执行离开一个范围时,所有在该范围中创建的自动对象都会被销毁
控制语句 | 描述 |
---|---|
break | 终止 loop 或 switch 语句,程序流将继续执行紧接着 loop 或 switch 的下一条语句。 简单说:在loop中使用break将跳出整个循环 |
continue | 跳出本轮循环,开始下一轮循环 |
1、数组
数组是一个存储相同类型元素的固定大小的顺序集合。数组是用来存储数据的集合,通常认为数组是一个同一类型变量的集合。
数组中重要的三个组成部分就是:元素(数组中的每一项都是一个元素),长度(数组的长度),索引(从0开始)
使用案例:
//声明数组 int[] nums; //初始化数组 double[] scores=new double[10]; //赋值给数组--逐个元素赋值 double[] doubles=new double[10]; doubles[0] = 10.1; doubles[1] = 10.2; //初始化并赋值给数组,可省略数组长度 double[] doubles1 = new double[]{10.1,10.2 }; //简化赋值过程 double[] doubles2 = { 10.1, 10.2, 10.3 }; //访问数据元素,通过索引 double num = doubles2[0]; //不过大多时候,都是使用for 和foreach 来操作数据 for (int i = 0; i < doubles2.Length; i++) { Console.WriteLine($"元素doules[{i}]={doubles2[i]}"); } Console.ReadLine();
数组扩展内容见:C#数组详解
2、字符串
在 C# 中,您可以使用字符数组来表示字符串,但是,更常见的做法是使用 string 关键字来声明一个字符串变量。string 关键字是 System.String 类的别名。
关于字符串的详细内容可见:C#字符串使用详解
3、结构体
在 C# 中,结构体是值类型数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。struct 关键字用于创建结构体。
结构体特点如下:
另外结构体与类的区别
代码如下:
using System; using System.Text; 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); } }; public class testStructure { public static void Main(string[] args) { 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(); Console.ReadKey(); } }
4、枚举
枚举是一组命名整型常量。枚举类型是使用 enum 关键字声明的。
C# 枚举是值类型。换句话说,枚举包含自己的值,且不能继承或传递继承。
使用案例如下:
//声明enum变量,不定义各项的值,默认 enum Days { Sun, Mon, Tue, Wed, Thu, Fri, Sat }; //声明enum变量,自定义各项的值 enum Day { Sun = 10,Mon = 20, Tue = 30, Wed = 40, Thu = 50, Fri = 60, Sat = 70, } //混合情况,第 n 个符号值与第 n-1 个有关,如:此时Mon为 11 enum WeekDay { Sun = 10, Mon, Tue, Wed = 40, Thu, Fri, Sat, } class Program { static void Main(string[] args) { //默认情况下,枚举中的值与int相互转换,从0开始依次增加 int x = (int)Days.Sun; int y = (int)Days.Fri; Console.WriteLine("Sun = {0}", x);//Sun = 0 Console.WriteLine("Fri = {0}", y);//Sun = 5 Day day = (Day)20;//将int值转化为枚举 Console.WriteLine(day.ToString());//将枚举转换成字符串,结果:Mon //将字符串转化为枚举 Day day1 = (Day)Enum.Parse(typeof(Day), "Mon");//如果Mon在不在枚举内,这种情况下会报错 //混合情况,第 n 个符号值与第 n-1 个有关。 int dy = (int)WeekDay.Mon; int dx= (int)WeekDay.Tue; int dz= (int)WeekDay.Sat; Console.WriteLine("WeekDay\tMon = {0}", dy);//WeekDay Mon = 11 Console.WriteLine("WeekDay\tTue = {0}", dx);//WeekDay Tue = 12 Console.WriteLine("WeekDay\tSat = {0}", dz);//WeekDay Sat = 43 Console.ReadLine(); } }
关于枚举的进阶用法,详情可见:C#枚举进阶用法
C#中的修饰符都是关键字
C# 封装根据具体的需要,设置使用者的访问权限,并通过 访问修饰符 来实现。
一个 访问修饰符 定义了一个类成员的范围和可见性。
常用修饰符还有:
static、abstract、virtual、override、sealed、readonly、extern、event、const、async、in、new、out
不常用的修饰符有:unsafe、volatile
这里会对部分修饰符的使用进行整理汇总,其余部分如果在后续深入的过程中涉及到也会逐步整理出来
- 使用 static 修饰符可声明属于类型本身而不是属于特定对象的静态成员。 static 修饰符可用于声明 static 类。 在类、接口和结构中,可以将 static 修饰符添加到字段、方法、属性、运算符、事件和构造函数。 static 修饰符不能用于索引器或终结器。
- 如果 static 关键字应用于类,则类的所有成员都必须为 static。
- 程序默认就创建实例,因此static对象无需实例化,直接使用;
- 类中静态变量,它只是定义在当前类中,但是本身不属于类,它早已经实例化,因此调用的时候直接使用类名 + 点(.)运算符进行调用
- 使用过多的静态对象,会比较消耗内存,因为它默认就实例化了,已经占好了内存
使用案例如下:
class UserInfo { private string _id; //静态变量 public static string Info; //普通变量 public string Id { get { return _id; } set { _id = value; } } } class Program { static void Main(string[] args) { //使用静态变量,无需实例化,直接通过类名. string info = UserInfo.Info; //使用普通变量,需要先实例化才可以 string id = new UserInfo().Id; Console.ReadLine(); } }
抽象方法
- 抽象方法是隐式的虚拟方法,abstract方法默认自带virtual属性
- 只有抽象类中才允许抽象方法声明。
- 由于抽象方法声明不提供实际的实现,因此没有方法主体;方法声明仅以分号结尾,且签名后没有大括号 ({ })。
- 在抽象方法声明中使用 static 或 virtual 修饰符是错误的。
抽象方法如下:
public abstract void GetInfo();
//实现由方法 override 提供,它是非抽象类的成员。
抽象类
- 抽象类不能实例化。
- 必须有子类继承
- 派生自抽象类的非抽象类,必须包含全部已继承的抽象方法和访问器的实际实现。
(简单说:子类必须实现抽象父类的所有抽象方法)
- 在抽象方法声明中使用 static 或 virtual 修饰符是错误的。
sealed的作用
- 在类声明中使用sealed可防止其它类继承此类;
- 在方法声明中使用sealed修饰符可防止子类重写此方法。
- sealed修饰符主要用于防止非有意的派生,但是它还能促使某些运行时优化
- 密封类中永远不可能有任何派生类。如果密封类实例中存在虚拟成员函数,该成员函数可以转化为非虚的,函数修饰符virtual 不再生效。
首先在C#中对于函数和方法并没有明确的定义区分,本质上是一样的,只是叫法不同;某些情况下,如我们会更习惯叫构造函数,而不叫构造方法。
熟悉方法无非两点:定义方法和调用方法。
定义方法:
<Access Specifier> <Return Type> <Method Name>(Parameter List)
{
Method Body
}
访问修饰符 方法修饰符 返回类型 函数名( 参数列表 )
{
函数体;
}
//实例如下:
public void GetInfo(string id)
{
//....
}
调用方法:
调用:通过方法名调用方法,如
Rectangle rectangle = new Rectangle();
//Display即是矩形对象中的方法,通过方法名调用即可
rectangle.Display();
认识一下构造函数,代码如下:
class Student
{
public string Name { get; set; }
public double Score { get; set; }
//构造函数,没有任何返回类型
public Student()
{
}
}
构造函数分为:默认/隐式构造函数 和参数化构造函数
class Student { public string Name { get; set; } public double Score { get; set; } //默认构造函数,没有任何返回类型 public Student() { } //参数化构造函数 public Student(string name) { } //参数化构造函数 public Student(string name, double score) { } }
终结器(以前称为析构函数)用于在垃圾回收器收集类实例时执行任何必要的最终清理操作。 在大多数情况下,通过使用 System.Runtime.InteropServices.SafeHandle 或派生类包装任何非托管句柄,可以免去编写终结器的过程。
class Car
{
~Car() // finalizer
{
// cleanup statements...
}
}
public class Destroyer
{
public override string ToString() => GetType().Name;
~Destroyer() => Console.WriteLine($"The {ToString()} finalizer is executing.");
}
不应使用空终结器。 如果类包含终结器,会在 Finalize 队列中创建一个条目。 此队列由垃圾回收器处理。 当 GC 处理队列时,它会调用每个终结器。 不必要的终结器(包括空的终结器、仅调用基类终结器的终结器,或者仅调用条件性发出的方法的终结器)会导致不必要的性能损失。
程序员无法控制何时调用终结器,因为这由垃圾回收器决定。 垃圾回收器检查应用程序不再使用的对象。 如果它认为某个对象符合终止条件,则调用终结器(如果有),并回收用来存储此对象的内存。 可以通过调用 Collect 强制进行垃圾回收,但多数情况下应避免此调用,因为它可能会造成性能问题。
顾名思义就是类中定义的类。
内部类的目的 是为了隐藏一个类中的类 ,保护内部的类
接口本身并不实现任何功能,它只是和声明实现该接口的对象订立一个必须实现哪些行为的契约。接口更像是抽象方法的一个集合
interface IDataLink
{
void Contect();
void DisContect();
}
相同点:
不同点:
其余涉及到面向对象编程的内容,如:封装,继承,多态等将在下一篇博客中进行展开~
以上就是本文的内容,希望以上内容可以帮助到您,如文中有不对之处,还请批评指正。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。