学习视频:https://b23.tv/jW9rNY 来源:b站
笔记介绍:在有编程基础上的C#学习,简单记录,便于自我复习。除C#知识外多记录了些没有学好的编程基础知识目录
学习视频:https://b23.tv/jW9rNY 来源:b站笔记介绍:在有编程基础上的C#学习,简单记录,便于自我复习。除C#知识外多记录了些没有学好的编程基础知识
代码示例1 “hello world”:输入输出函数,格式含义
代码示例3 :数据类型转换 从字符串中获取每个字符 随机数 字符串
代码示例12:C#泛型集合 List 字典集合 Dictionary
(一).NET简介
.NET dotnet :Microsoft 新一代多语言的开发平台,用于构建和运行应用程序。
主要由两部分组成:公共语言进行时;类库
编译运行过程:源代码(cs文本)---CLS编译-----中间语言(exe dll)------CLR编译------机器码01
CLS编译目的:跨语言 CLR编译目的:优化 跨平台
语言 | ||
CLS 公共语言规范(定义了.NET平台上运行的语言所必须支持的规范,避免语言特性产生错误) | ||
类库 | ASP.NET Web窗体 Web服务 | Windows 窗体 |
ADO.NET &XML | ||
基类库 | ||
CLR公共语言运行库(程序的运行环境,负责内存分配、垃圾收集、安全检查等) | ||
操作系统 |
C# csharp :微软为.NET推出的高级编程语言。
Mono:支持在其他操作系统下开发.NET框架。
Unity借助Mono实现跨平台,核心是.NET Framework框架。
(二)变量
容量的单位:
位:记忆体最小单位;字节:存储最小单位;网速10M指的是Mbps(兆位/秒),速率单位,相当于10/8兆字节/秒。
整数:
| 有符号 | 无符号 |
1个字节 | Sbyte(-128~127) | Byte(0~255) |
2个字节 | Short(-32768~32767) | Ushort(0~65535) |
4个字节 | Int | Uint |
8个字节 | Long | Ulong |
非整数:默认double,要加后缀
4个字节 | 单精度浮点数float | 7位 |
8个字节 | 双精度浮点数double | 15-16位 |
16个字节 | 128位数据类型decimal | 28-29位 |
非数值型:
char | 字符,2字节 | 存储单字符,使用单引号
|
String | 字符串 | 存储文本,使用双引号 |
Bool | 判断,1字节 | 真1假2 |
(三)语法
声明:变量类型 变量名;
命名规则:有字母、数字、下划线;
(四)数据类型
1.类型分类
通用类型系统CTS是.NET框架中的一个组成部分
A值类型 :存储数据本身
a.结构(数值类型、bool、char)
b.枚举
B引用类型:存储数据的引用(内存地址)
a.接口
b.类(string、Array、委托)
2.内存:CPU与其他外部存储器交换数据的桥梁
分配:
A栈区:空间小(1MB),读取速度快;用于存储正在执行的方法,分配的空间叫做栈帧用于存储方法中的参数和变量等。执行完毕后清除栈帧。
B堆区:空间大,读取速度慢;用于存储引用类型的数据。
3.局部变量
定义在方法内部的变量;没有默认值必须设置初始值;方法被调用时存在栈里,结束清除。
局部变量中---->
值类型:声明在栈中,数据存储在栈
引用类型:声明在栈中,数据存储在堆中,栈中存储该数据的引用
(分配图:关于此部分可看视频 了解空间存储,区分修改的是栈中数据还是堆中数据)
垃圾回收器:GC是CLR中一种针对托管堆自动回收释放内存的服务。栈中引用判断内存是否使用,不使用则回收(null)
4.方法执行在栈中,执行完毕清除栈帧。
5.比较:数组比较与整数比较,注意比较对象
6.赋值
7.传参
(五)枚举
列举某种数据的所有取值
作用:增强代码可读性,限定取值
语法:enum 名字{值1,值2,值3}。
枚举元素默认为int,准许使用byte、sbyte、short、ushort、int、uint、long、ulong。
每个枚举元素都有枚举值,默认情况下第一个枚举的值为0,后面每个枚举的值递增1,可修改。
(六)类和对象
面向对象:一种软件开发思想。指导程序员如何分析解决问题
类:抽象概念,“类别”
对象:类的具体实例,“某类别的个体”
名词类型共性:数据成员
动词类型共性:方法成员
语法: 访问级别 class 类名
{
.......类成员......
字段:存储数据
属性:保护字段
构造函数:听过创建对象的方式,初始化类的数据成员
方法:向类的外部提供某种功能
}
每个类都在一个独立的C#源文件中,创建新的类意味着在当前项目中产生了一种新的数据类型。
声明在栈中,对象在堆里,对象的数据成员也在堆里。
下面是代码示例10的内存分配情况,蓝色为第一部分代码 橙色为第二部分的修改情况
(七)成员变量
定义在类中方法外的变量
特点:具有默认值;所在类被实例化后存在堆中。对象被回收时,成员变量从堆中清除;可以和局部变量重名,优先查找局部变量。
属性:对字段起保护作用,可实现只读、只写功能。
本质就是对字段的读取和写入方法。
属性语法:
[访问修饰符] 数据类型 属性名
{
get{return 字段;}
set{字段=value;}
}
通常一个公有属性和一个私有字段对应,属性只是外壳,实际上操作的是私有字段。
构造函数:提供了创建对象的方式,常常用于初始化类的数据成员。
如果不希望在类的外部创建对象,就将构造函数私有化。
特点:没有返回值,与类同名,只有在new的时候才能调用;一个类若没有构造函数,编译器会自动生成一个无参数构造函数。
语法:
[访问修饰符] 类名([参数列表])
{
初始化......
}
this关键字:表示当前对象的引用,访问当前类成员时,使用this关键字;可以提高代码的可读性,在没有歧义的情况下可以省略。
结构struct:
结构属于值类型适合轻量级,类属于引用类型。
结构体不能包含五参数的构造函数,因为其自带。
其构造函数中,必须先为所有字段赋值。
const:常量。
新建项目:
VS2019创建空白解决方案;添加新建项目,选择 控制台应用(.NET Framework);
新建类:
右键添加类
代码示例1 “hello world”:输入输出函数,格式含义
//引入命名空间
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
//Ctrl+A全选
//Ctrl+K+F 排版
//Ctrl+K+C 注释
//Ctrl+K+U 取消注释
//定义命名空间【类的住址】;逻辑划分,避免重名
namespace Day01
{
//定义类【做工具】
class Program
{
//定义方法【做功能】;程序的入口方法
static void Main(string[] args)
{
//console是类【工具】;WriteLine/Readline是方法【动词的功能】;Title是属性【名词的修饰】;类.方法()调用语句【使用某个类中的功能】;
Console.Title = "My first cs";
Console.WriteLine("hello world");//控制台.写一行(“”);将括号里面的文本写入控制台
string input = Console.ReadLine();
Console.WriteLine("hi" + input);
Console.Write("welcom");//不换行输出
Console.ReadLine();//控制台.读一行;暂停程序直到输入回车;将用户在控制台输入的文本,读到程序中来
}
}
}
代码示例2 :占位符 标准数字格式字符串 转义符
namespace Day01
{
class Program
{
static void Main(string[] args)
{
//占位符
string gunName="AK";
string ammoCapacity = "30";
string str=string.Format("枪的名称:{0},容量: {1}",gunName, ammoCapacity);//str的格式为format(); 占位符{位置的编号}
Console.WriteLine(str);
//标准数字格式字符串
Console.WriteLine("金额:{0}", 10);//金额:10
Console.WriteLine("金额:{0:c}",10);//金额:¥10.00
Console.WriteLine("金额:{0:d2}", 5);//金额:05【不足两位0填充】
Console.WriteLine("金额:{0:f1}", 1.26);//金额:1.3【根据指定精度显示】
Console.WriteLine("金额:{0:p}", 0.1);//金额:10.00%【百分数及其精确位数】.
Console.WriteLine("金额:{0:p0}", 0.1);//金额:10%
Console.WriteLine("金额:{0:p1}", 10);//金额:10.0%
//转义符 改变字符原始含义 \"【"】 \'【'】 \0【 】
Console.WriteLine("我爱\"unity\"!!");
// \r\n【回车换行】 \t【水平制表格】
Console.WriteLine("我爱\r\nunity!!");
}
}
}
代码示例3 :数据类型转换 从字符串中获取每个字符 随机数 字符串
namespace Day01
{
class Program
{
static void Main(string[] args)
{
//Parse转换 :string转换为其他数据类型
//待转数据必须“像”该数据类型
string strNumber = "12";
int strNumber1 = int.Parse(strNumber);
//任意类型转换为string类型
int number = 10;
number.ToString();
//从字符串中获取每个字符
#region vs专用,不是cs的语法 方便折叠
string str1 = "123";
char c1 = str1[0];//'1'
string s1 = c1.ToString();//"1"
#endregion
//隐式转换 由小范围到大范围的自动转换
byte b3 = 100;
int i3 = b3;//i3=100
//显示转换 由大范围到小范围的强制转换 可能发生精度丢失
int i4 = 100;
byte b4 = (byte)i4;//i3=100
//多种变量参与计算 结果自动向较大的类型 快捷运算符不做类型提升,超出为
float number1 = 1;
double number2 = 2;
int result = (int)(number1 +number2);
//随机数
Random random = new Random();//创建一个随机数工具
int rnumber = random.Next(1, 101); //产生一个随机数
//字符串插入
string str = "myname";
str.Insert(4, "?");
//查找指定字符在字符串中的索引
int index=str.IndexOf('y');//index=1
//其他字符方法
str.Replace("my","you");//替换
str.Remove(2);//从第二个开始都删掉
str.StartsWith("myna");//比较开头一样与否
str.Contains("name");//是否包含
}
}
}
代码示例4 :数组 二维数组
namespace Day01
{
class Program
{//数组
static void Main(string[] args)
{
//声明
int[] a;
//初始化 new 数据类型[容量]
a = new int[6];
//通过索引读写每个元素
a[0] = 1;
//声明并赋值
float[] b = new float[3] {1,2,3};
//循环遍历数组
for(int i = 0; i < a.Length; i++)
{
Console.WriteLine(a[i]);
}
//foreach 从头到尾依次读取数组元素
foreach(int item in a)
{
Console.WriteLine(item);
}
//推断类型:根据所赋数据,推断类型
var v1 = 1;//int
Array arr = new int[2];
//声明object类型 赋值任意类型
object o1 = 1;
//数组方法
//indexof 查找元素
int[] arr2 = new int[]{ 1, 2, 3, 4 };
int index = Array.IndexOf(arr2, 2);//index=1
//复制 克隆 排序
int[] arr3 = new int[] { 5, 6, 7, 8 };
Array.Copy(arr2, arr3, 2);//从arr2第一个元素选取两个复制到arr3
object obj=arr3.Clone();//克隆了一个数组arr3给obj
Array.Sort(arr2);//对arr2进行从小到大排序
Array.Reverse(arr2);//反转 从大到小排序
//二维数组
int[,] map = new int[4, 4] { { 2, 2, 1, 1 }, { 1, 1, 2, 2 }, { 3, 3, 4, 4 }, { 4, 4, 3, 3 } };
}
}
}
代码示例5 :数组传参
namespace Day01
{
class Program
{
//数组传参
static void Main(string[] args)
{
Add(new int[] { 1, 2, 3, 4 });//没有params时传递
//params 参数数组 对于方法内部而言就是一个普通数组
//对于方法外部(调用者)而言 是可以传递数组,传递一组数据类型相同的变量集合
//甚至可以不传递参数
Add(1, 2, 3, 4);
Add();
}
private static int Add(params int[] arr)
{
int arr1 = arr[1];
return arr1;
}
}
}
代码示例6 :传参
namespace Day01
{
class Program
{
//数据类型
static void Main(string[] args)
{
//值类型 引用类型
//int bool char string Array
//方法执行在栈中 所以在方法中声明的变量都在栈中
//
int b = 2;
int a = 1;
int[] arr = new int[] { 1 };
Fun1(a, arr);//传递实参变量存储的内容
Fun2(ref a);
Fun3(out a);
Fun4(a, out b);//组合情况
//tryparse方法,返回2个结果 out:转换后的结果 返回值:是否可以转换
int result;
bool re=int.TryParse("250", out result);//判断是否可以转int
}
//值参数:按值传递-传递实参变量的存储的内容
//作用是传递信息
private static void Fun1(int a,int[] arr)
{
a = 2;
}
//引用参数:按引用传递-传递实参变量自身的内存地址,方法内部修改
//作用是改变数据
private static void Fun2(ref int a)
{
a = 3;
}
//输出参数:按引用传递-传递实参变量自身的内存地址,方法内部修改
//区别1:方法内部必须为输出参数赋值
//区别2:输出参数传递之前可以不赋值
//作用是接收结果
private static void Fun3(out int a)
{
a = 4;
}
private static void Fun4(int a,out int b)
{
a = 4;
b = 3;
}
}
}
代码示例7 :拆装箱
namespace Day01
{
class Program
{
static void Main(string[] args)
{
//拆装箱
//装箱操作:值类型到object类型的隐式转换
//内部机制: 到堆里面开辟内存空间 将值类型的数据复制到堆中 返回堆中新分配对象的地址
//int 同步块索引 类型对象指针 三步 相对来讲“最”消耗性能
int a = 1;
object o = a;
//拆箱操作:从object类型到值类型或从接口类型到实现该接口的值类型的显示转换
//内部机制:判断给定类型是否是装箱时的类型 返回已装箱实例中属于原值类型字段的地址
//消耗性能
int b = (int)o;
int num = 0;
string str = num.ToString();//没有拆装箱
string str2 = "" + num;//有拆装箱 相当于string.Concat("", num); 形参object ,实参int类型
}
//形参object ,实参传递值类型,则装箱
//可以通过重载、泛型 避免
private static void Add(object obj)
{
}
}
}
代码示例8 :字符串池 字符拼接
namespace Day01
{
class Program
{
static void Main(string[] args)
{
string s1 = "hi";//写死的字符串常量
string s2 = "hi";
bool r1 = object.ReferenceEquals(s1, s2);//true 两者是同一个字符串
string s3 = new string(new char[] { 'h', 'i' });
string s4 = new string(new char[] { 'h', 'i' });
bool r2 = object.ReferenceEquals(s3, s4);//false 两者不是同一个字符串
//字符串池
//写死的字符串常量放入字符串池,再创的时候查找有没有相同的文本“hi”,如果有则返回对象引用
//提高利用率
//字符串的不可变性
s1 = "oh";//重新开辟空间存储新字符串 替换栈中引用 防止其他对象内存被破坏
object o1 = 1;
o1 = 2.0;//重新开辟空间存储
//字符拼接
//方法一:每次拼接会产生新的对象,替换引用,产生大量垃圾
string strNumber = "";
for (int i = 0; i < 10; i++)
{
strNumber = strNumber + i.ToString();
}
//方法二:StringBuilder 可变字符串,可以在原有空间上进行修改
//适用于频繁对字符串操作
StringBuilder builder = new StringBuilder(10);//10 尽量准确 超过的话会新开辟一个空间拷贝原有的,进的来但是会产生更大的垃圾
for(int i = 0; i < 10; i++)
{
builder.Append(i);
}
string result = builder.ToString();
builder.Insert(0,"builder");//不需要s1=s1.Insert(); 可以直接在原有空间修改
//builder.Replace();
//builder.Remove();
}
}
}
代码示例9 :枚举
namespace Day01
{
class Program
{
[Flags]//允许多选的标记
public enum Move : int
{
Up = 0,//1
Down = 1,//2
Left = 2,//4
Right = 3//8
}
static void Main(string[] args)
{
MoveStyle(Move.Down);//
MoveStyle(Move.Down|Move.Left);//选择多个枚举值,运算符| 按位运算 1个为1则为1
//条件:
//1.任意多个枚举值做按位运算的时候,结果不能与其他枚举值相同(所以最好用2的n次方赋值)
//2.定义枚举时候,使用【Flags】特性修饰
//数据类型转换
//int=>enum
Move style2 = (Move)2;
MoveStyle(style2);
//enum=>int
int number =(int) (Move.Up|Move.Down);
//string=>enum
Move style3=(Move)Enum.Parse(typeof(Move), "Up");
//enum=>string
string strEnum = Move.Left.ToString();
}
private static void MoveStyle(Move style)
{
if ((style & Move.Down)== Move.Down)//包含情况 用按位与&
{
Console.WriteLine("Down");
}
if ((style & Move.Up) == Move.Up)
{
Console.WriteLine("Up");
}
if (style == Move.Left)
{
Console.WriteLine("Left");
}
if (style == Move.Right)
{
Console.WriteLine("Right");
}
}
}
}
代码示例10 :类和成员变量 属性
//类中代码
namespace Day01
{
class Wife
{
//数据成员
//不是所有编程语言中都有private 便于统一
private string sex;
private int age;
private string name;
//属性:保护字段 本质就是2个方法
public string Name
{
//读取时保护
get
{ return name;
}
//写入时保护 Value要设置的数据
set
{
this.name = value;
}
}
public int Age
{
//读取时保护
get
{
return age;
}
//写入时保护 Value要设置的数据
set
{
if (value <= 19)
this.age = value;
else
throw new Exception("No!");
}
}
//构造函数
public Wife(string name,int age):this(name)//执行wife(name);
{
// this.name = name;//构造函数如果为字段赋值,属性中的代码块不会执行
this.Age = age;
}
public Wife(string name):this()//执行wife();
{
//调用无参数的构造函数
this.name = name;
}
public Wife()
{
Console.WriteLine("有wife被创建啦");
}
//方法成员
public void SetName(string name)
{
//this这个对象(引用)
this.name = name;
}
public string GetName()
{
//this这个对象(引用)
return name;
}
public void SetAge(int age)
{
//this这个对象(引用)
if (age <= 19)
this.age = age;
else
throw new Exception("No!");
}
public int GetAge()
{
//this这个对象(引用)
return age;
}
}
}
namespace Day01
{
class Program
{
static void Main(string[] args)
{
/****第一部分*****/
//声明Wife类型的引用
Wife wife01;
//指向wife类型的对象(实例化wife类型对象)
wife01 = new Wife();
wife01.SetName("Alice");
wife01.SetAge(18);
Console.WriteLine(wife01.GetName());
Console.WriteLine(wife01.GetAge());
/****第二部分*****/
Wife wife02 = wife01;
wife02.SetName("Linda");
Console.WriteLine(wife01.GetAge());
/****第三部分*****/
Wife wife03 = new Wife();
wife03.Name = "selina";//执行Name的set
wife03.Age = 30;//执行Age的set
Console.WriteLine(wife03.Name);//执行Name的get
/****第四部分:构造函数*****/
Wife wife04 = new Wife("Cindy",25);
Wife wife05 = new Wife("Baby", 24);
}
}
}
代码示例11:类的数组
namespace Day01
{
class Program
{
static void Main(string[] args)
{
Wife[] WifeArray = new Wife[5];//初始化必须指定大小
WifeArray[0] = new Wife("01", 40);//读写元素必须通过索引
WifeArray[1] = new Wife("02", 41);
WifeArray[2] = new Wife("03", 42);
WifeArray[3] = new Wife("04", 43);
WifeArray[4] = new Wife("05", 44);
}
}
}
代码示例12:C#泛型集合 List 字典集合 Dictionary
namespace Day01
{
class Program
{
static void Main(string[] args)
{
//C#泛型集合 List<数据类型> 根据索引查找
List<Wife> list1 = new List<Wife>(2);
list1.Add(new Wife());
//list1.Insert();
//list1.Remove();
Wife wife2 = list1[0];
//读取所有元素
for(int i = 0; i < list1.Count; i++)
{
Wife w = list1[i];
}
//字典集合 根据键值查找
Dictionary<string, Wife> dic = new Dictionary<string, Wife>();
dic.Add("an", new Wife("angel",55));
Wife anWife = dic["an"];
}
}
}
代码示例13:继承、静态
namespace Day01
{
class Program
{
static void Main(string[] args)
{
//----继承--------------------------------//
//子类可以使用父类成员 父类只能使用父类
//子类继承了父类的成员 但是有访问级别的限制 private用不了
student stu01 = new student();
stu01.Name = "";
//父类型的引用指向子类型的对象 只能使用父类成员
Person person01 = new student();
student stu02 = (student)person01;//需要访问子类的成员需要强制类型转换
int a =stu02.Score;
//Teacher teacher01 = (Teacher)person01;//异常
//Teacher teacher01 = person01 as Teacher;//异常
//----static---------------------------//
//静态类、静态方法、静态字段
//静态成员变量:属于类 只有一份 早出现; 实例成员变量属于对象,创建初始化,每个对象一份
//静态类:不能实例化,只能包含静态成员,静态类不能被继承但是静态方法、属性可以被
student stu11 =new student();//0=>1
student stu12=new student();//0=>1
student stu13 = new student();//0=>1
Console.WriteLine(stu13.InstantCount);//1//原因:每个对象里面都有一个instantcount
Console.WriteLine(student.StaticCount);//3
//----结构体--------------------------------//
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Day01
{
class Person
{
public string Name { get; set; }
//私有 无法使用
private string sex;
//仅仅本家族使用
protected int age;
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Day01
{
class Teacher
{
public int Salary { get; set; }
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Day01
{
class student : Person
{
//prop+tab+tab
public int Score { get; set; }
//实例成员
public int InstantCount;
//静态成员
public static int StaticCount;
//实例构造函数作用:提供创建对象的方式,初始化累的实例数据成员
public student()
{
InstantCount++;
StaticCount++;
}
//静态构造函数
//作用:初始化类的静态数据成员
//执行时机:类加载的时候调用一次
static student()//静态构造函数不能有访问级别
{
}
//静态方法
static public void fun2()
{
//非静态字段 要求对象引用 否则只能访问静态成员
//
}
}
}