C#语法和类型转换
表达式 | 说明 |
---|---|
x.m | 成员访问 |
x(…) | 方法和委托调用 |
x[…] | 数组和索引器访问 |
new T(…){…} | 使用初始值设定项创建对象 |
new T(…) | 对象和委托创建 |
new {…} | 匿名对象初始值设定项 |
类型转换
as类型转换:将一个对象转换为指定类型,成功返回转换后的对象,失败就返回null、常用于引用类型之间的转换
expression as type expression是进行转换的表达式,type是目标类型
class Animal
{
public void Eat()
{
Console.WriteLine("Animal is eating.");
}
}
class Dog : Animal
{
public void Bark()
{
Console.WriteLine("Dog is barking.");
}
}
class Program
{
static void Main()
{
Animal animal = new Dog(); //animal转换成Dog类型
Dog dog = animal as Dog;
if (dog != null)
{
dog.Bark();
}
else
{
Console.WriteLine("Conversion failed.");
}
}
}
函数
普通函数/方法的定义
//定义普通函数(不需要return)
class Proge
{
static void username(string name)
{
//校验用户名的方法
if (name == null)
{
Console.WriteLine("用户名不能位空");
}else if(name.Length > 5)
{
Console.WriteLine("用户名长度不能超过6个");
}
else
{
Console.WriteLine("你的用户名是:" + name);
}
}
//下面是主函数的的入口方法
static void Main(string[] args)
{
string name = Convert.ToString(Console.ReadLine());
username(name);
}
}
指定类型函数的定义
//指定方法类型定义 (需要通过return去处理结果)
static int sum(int a, int b)
{
int res = a + b;
return res;
}
static void Main(string[] args)
{
Console.WriteLine(sum(1, 2)); //结果3
int a = Convert.ToInt32(Console.ReadLine());
int b = Convert.ToInt32(Console.ReadLine());
Console.WriteLine(sum(a, b));
}
数组
C#的数组声明跟JAVA一样,但是它不像JAVA,[]只能放在类型后面
int[] ages = {...};
int[] ages;
ages = new int[]{...};
其余的跟Java没什么区别
参数数组
在无法确认参数是否有具体个数的情况下使用,可以传递任意个个数的参数
具体的写法: params int[] array
用例场景:(任意多个参数相加)
static int Add2(int[] array)
{
int sum = 0;
foreach(int i in array)
{
sum += i;
}
return sum;
}
static void Main(string[] args)
{
int sum1 = Add2(new int[]{ 1, 2, 3 });
Console.WriteLine(sum1);
}
// 上方代码所表示的是我们自己构建的数组,通过我们自己构建的数组去执行多个参数的使用方法
//这样会比较麻烦,所以才会使用到参数数组
//参数数组
static int Add3( string name,params int[] array) // 加上params就代表这个数组是一个参数数组
{
int sum = 0;
foreach (int i in array)
{
sum += i;
}
return sum;
}
static void Main(string[] args)
{
int sum3 = Add3("skd",6,8,11);
Console.WriteLine(sum3); //25
}
//相比于我们自己构建的数组写法更加的简便也更直观
//而且参数数组跟普通构建数组一样是可以跟其他的类型参数进行混搭的
函数的重构
重构的关键点就在于,方法的名是可以相同,但是它们之间的参数要不一样
class password
{
//函数的重构Overload 函数名可以相同,但是函数的参数不能相同 这就是函数重载
static int Max(params int[] intarray)
{
int sum = 0;
for (int i = 0; i < intarray.Length; i++)
{
sum += intarray[i];
}
return sum;
}
static double Max(params double[] doublearray)
{
double sum = 0;
for(double i=0; i<doublearray.Length; i++)
{
sum = doublearray[(int)i];
}
return sum;
}
static void Main(string[] arges)
{
Console.WriteLine(Max(1, 2, 3.5)); //6.5
Console.WriteLine(Max(1, 2, 3));//6
}
}
枚举
枚举:相当于是一组命名整型的常量,通过enum关键字去声明
(枚举相当于就是我们自己所去创建的类型,而不是系统自己已经定义好的类型)
class parar
{
//枚举的定义 通过函数去定义
enum Game { //game枚举的类型名字
Mage,Archer,Assassin,Tank,Support,Warrior //值
}
static void Main(string[] args)
{
//1法师 (Mage) 、2射手 (Archer) 、3刺客 (Assassin) 、4坦克(Tank) 、5辅助 (Support)、6战士(Warrior)
Game game = Game.Mage; //这样去使用枚举中的值
}
}
枚举的使用场景:表示游戏的状态
enum GameState
{
Menu,Running,Stop,success
}
static void Main(string[] args)
{
GameState game = GameState.Menu;
game = GameState.Running; //游戏状态:游戏中
//或者直接输出它现在的游戏状态是什么
Console.WriteLine(game);//running
}
结构体
个人感觉结构体其实相当于就是一个用来存储一个数据类型的容器
结构体的特点:
结构体可以带有方法、字段、事件…
结构体不能继承
结构体可以实现多个接口
结构体可以定义构造函数,但是不能定义解析函数
struct Book
{
public string title;
public string author;
public string subject;
public int book_id;
};
public static void Main(string[] args)
{
Book book = new Book();
//对book的信息描述
book.title = "崩坏3";
book.author = "海的女儿";
book.subject = "希儿";
book.book_id = 1;
//打印信息
Console.WriteLine("故事的总称:" + book.title);
Console.WriteLine("故事的章节:" + book.author);
Console.WriteLine("故事的主角:" + book.subject);
Console.WriteLine("故事的编号:" + book.book_id);
}
//结构体就是容器的属性,结构体通过类型的控制去对容器中的数据进行限制
结构体函数
当在结构体内定义函数,那么这个函数是可以直接访问结构体内的所数据(可以理解成同源)
class pasd
{
struct Book
{
public string title;
public string author;
public string subject;
public int book_id;
};
//结构体函数
struct NewBook {
//在结构体内编写一个函数方法
public string name;
public int age;
public string sex;
public void Printshow()
{
Console.WriteLine("你的姓名是:" + name+"," + "你的性别是:" + sex + ","+"你的年龄是:" + age);
}
}
public static void Main(string[] args)
{
//NewBook kid = new NewBook();
//创建对象的缩写
NewBook kid;
kid.name = "柯南";
kid.age = 10;
kid.sex = "男";
kid.Printshow();
委托(实话说有点理解不了后续进行理解)
委托可以让我们把方法作为参数去进行传递
//委托的声明
//通过delegate关键字去声明
delegate void MyDelegate();
public static void Main(string[] args)
{
void Method1()
{
Console.WriteLine("Method1");
}
MyDelegate delegateInstance = new MyDelegate(Method1); //这里是绑定方法
delegateInstance();
//也可以调用匿名的方法
MyDelegate delegateInstance = delegate()
{
Console.WriteLine("Anonymous method");
};
}
//大概就这个写法
异常的捕抓
C#的异常捕抓其实是跟前端差不多的,通过try catch finally去捕获异常
static void Main(string[] args)
{
try
{
int n1 = Convert.ToInt32(Console.ReadLine());
int n2 = Convert.ToInt32(Console.ReadLine());
}
catch (FormatException e)
{
Console.WriteLine("不能输入除数字以外的字符");
}
}
//写法跟前端一样
构造函数
构造函数的特点:
与类名相同、没有return、可以重载、自动调用、可以包含初始化代码
构造函数的声明写法
class Person
{
private string name;
private int age;
/* public Person()
{
//构造函数默认的写法
name = "Unknown";
age = 0;
}
*/
public Person(string n,int a)
{
//带参数的构造函数写法
name = n;
age = a;
}
public void work()
{
Console.WriteLine("名字" + name);
Console.WriteLine("年龄" + age);
}
//后面会跟着get 和set方法用来获取数据以及修改后的数据
static void Main(string[] args)
{
Person person = new Person("王楚然",23);
person.work();
}
}
属性
首先引入Get和Set方法
Get和Set有两种写法,一种是写在构造函数外面、一种是写在构造函数内部的
Customer.cs
//public void SetAge(int age)
//{
// this.age = age;
//}
//public int GetAge()
//{
// return age;
//}
//get 和set 方法通常是用来访问对象属性的
//访问和设置,基本是连着构造函数使用的
public int Age
{
set
{
//设置值value
this.age = value;
}
get
{
//获取值
return age;
}
}
Program.cs
static void Main(string[] args)
{
Customer list = new Customer();
//get 和set写法的区别
/*
如果是分开单独来写
SetAge{this.age = age;}
GetAge{return age;}
那么去使用这2个方法就用下方的情况去写
list.SetAge(23);
Console.WriteLine(list.GetAge());*/
/*
如果是把set和get方法都写在构造函数内==> 写在构造函数内get和set不分上下级
public int Age{ get{return age;} set{this.age= value;}}
那么应用的写法 直接引用写就可以,写了参数之后会自动帮我们进行set 和get方法的构造
*/
list.Age = 10;
list.Name = "王楚然";
list.Sex = "女";
Console.WriteLine(list.Age);
Console.WriteLine(list.Name);
Console.WriteLine(list.Sex);
list.Show(); //调用类的方法都是大同小异的
}
属性的匿名类型
我们可以去控制属性的get和set,如果只是在属性中设置了get、那么我可以对这个属性进行读取,但是无法进行修改,相反来说如果只是设置set那么我们可以对这个属性进行修改,但是无法读取修改后的或者是修改前的值
如果只是单独的进行对属性的读写,get和set 可以通过 get;set;直接进行简写
还有一个点:我们设置变量通常都会对变量进行一个固定
比如说:int age = 21;
但是固定类型有弊端,有时候输入的数据并不是我们所指定的类型,这时候会使用var,
var可以说跟ts中的any一样,在不确定类型的情况下使用,var变量跟前端一样可以进行变量的提升
虽然c#中有var和const 但是没有let 全局变量所以不要搞混了
继承
继承的概念都差不多,但是C#继承的声明方法与其他编程语言有不同
如果是java就话那么继承通常会有extend这个关键字去提及
但是C#就直接使用:就吧他当作是一个继承了
class Driver:BseClass{}
小案例练习:
Enemy.cs
class Enemy
{
protected int hp;
protected string speed;
}
Boss.cs
class Boss:Enemy
{
private int hujia;
public Boss(int hujia,int hp,string speed)
{
this.hujia = hujia;
this.hp = hp;
this.speed = speed;
//base.hp = hp;
//base.speed = speed ;
}
//虽然hp和speed是通过继承所得到的,但是在继承之后其实子类本身就有这些变量
//需要我们通过this指向去指明变量
public void zhuangtai()
{
Console.WriteLine("HP:"+ hp);
Console.WriteLine("Speed:"+ speed);
Console.WriteLine("hujia:" + hujia);
}
}
Program.cs
class jicheng
{
static void Main(string[] args)
{
Boss boss1 = new Boss(100,200,"快");
boss1.zhuangtai();
}
}
关于小点
1、关于变量权限问题,public private protected
其中protected虽然也算是私有,但是protected所给的权限是子类可以访问父类
2、关于this和base关键字
this总所周知直接指向,而base也是专门给继承的子类的,base只能去访问父类的数据成员
虚方法
感觉虚方法相当于就是方法重写,通常使用的情况是父类的方法不适用于子类方法的时候才会去使用
Ktton.cs
class Ktton //父类
{
public virtual void Move()
{
//虚方法声明方式virtual
Console.WriteLine("搜索敌人");
}
}
Boss.cs
class Boss:Ktton
{
//然后继承过来之后我们想要重写父类方法可以通过关键字override来进行重构
public override void Move()
{
Console.WriteLine("Boss的移动方法");
}
}
Progarm.cs
class Enmay {
static void Main(string[] agrs)
{
//去调用Boss类的move方法得到的是重构的方法并不是父类所继承的方法
Boss boss = new Boss();
boss.Move();//Boss的移动方法
}
}
匿名方法和Lambda表达式
匿名方法:简单的方法(一两行代码的方法)就适用于它,声明为delegate
Lambda表达式的基本形式:
(input params)=>express
其中input、params为输入参数,express为表达式
static void Main(string[] args)
{
//匿名方法:简单方法(一两行代码的方法)适合用匿名方法,需要声明为delegate
Func<int, int, int> plus = delegate (int a, int b) { return a + b; };
//Func<int, int, int>:前两个参数类型是匿名方法的参数类型,第三个参数类型是返回值类型
//delegate 是一个匿名方法 2个参数 和参数使用的方法
int rusel = plus(1, 2);
Console.WriteLine(rusel);
//lambda表达式可以访问外部变量
int a = 5;
Func<double, double> fun = x => x + a;
Console.WriteLine(fun(5));
Console.ReadKey();
}
List与泛型的使用(集合类型)
List<>相当于就是把数据保存在一个组合里面,通过遍历去输入结果,跟原始方法,相当于一个是一个在盒子里面,一个是在盒子外面。目前对于我来说,可能当我使用LINQ语句才会使用到
public class Person
{
public int Age { get; set; }
public string sex { get; set; }
}
class Program
{
static void Main(string[] args)
{
List<Person> list = new List<Person>();
Person list1 = new Person
{ Age = 1, sex = "男" };
list.Add(list1);
Person list2 = new Person { Age = 2, sex = "女" };
list.Add(list2);
foreach (Person p in list)
{
Console.WriteLine("{0},{1}", p.Age, p.sex);
}
Console.WriteLine();
//Person list1 = new Person();
//list1.Age = 1;
//list1.sex = "男";
//Console.WriteLine(list1);
//Person list2 = new Person();
//list2.Age = 2;
//list2.sex = "女";
//Console.WriteLine(list2);
}
}
上方使用2种方法所实现的功能是相同,但是他们的区别在于如果是使用list<>的方法,适用于对象的创建和初始化在同一处完成情况,而第二种方法适用的比较灵活,而且可以通过覆盖来去更新数据,看情况使用吧
ArrayList、Dictionary的使用
ArrayList、Dictionary是集合类型,通常会使用泛型集合类List和Dictionary<TKey, TValue>来替代它们
class papr
{
private static object value;
static void Main(string[] args)
{
//Array是数组的意思,ArrayList相当于就是一个动态数组,可以随时进行存取
ArrayList list = new ArrayList();
list.Add(1);
list.Add(2);
list.Add(3);
object obj = list[0];
Console.WriteLine(obj);
//Dictionary是一种键值对集合,用于存储具有唯一键的元素
//Dictionary<TKey, TValue> 第一个参数就是键 后面的才是变量
Dictionary<string, int> dit = new Dictionary<string, int>();
dit.Add("Apple", 1);
//通过键去访问Dictionary的值
value = dit["Apple"]; //获取键值Apple
Console.WriteLine(value);
}
}
拓展:Dictionary<T1,T2>和Hashtable的异同
Dictionary<T1,T2>是HashTable的泛型版本,这一点在大致上是正确的,但仍然有区别看代码
Console.WriteLine("Dictionary:");
Dictionary<int, int> dic = new Dictionary<int, int>();
dic.Add(1, 5);
dic.Add(10, 3);
dic.Add(2, 5);
foreach (int key in dic.Keys)
{
Console.Write(key+" "); //1,10,2
}
Console.WriteLine();
Console.WriteLine("Hashtable:");
Hashtable hashtable = new Hashtable();
hashtable.Add(1, 5);
hashtable.Add(2, 3);
hashtable.Add(3, 5);
foreach (object key in hashtable.Keys)
{
Console.Write(key.ToString()+" "); //3,2,1
}
总结:一个是根据插入的顺序来去进行遍历,一个是在遍历之前自身内部进行一次从大到小的排序再进行遍历本质上差不多
常量、枚举
常量:const
枚举: 通过enum关键字去实现枚举定义 用到再百度
File类
1、Path类型 用于操作路径的
//获得指定路径下的文件名字
String path = @"E:\海天瑞声\9.6\podcast\0 - j1vtfQrCU__0001.wav";
//获得文件名
string str = Path.GetFileName(path);
/*
使用截取的方法获取文件名:
int index = path.LastIndexOf("\\");
path = path.Substring(index + 1);
Console.WriteLine(path);
*/
Console.WriteLine(str);
//获取不带扩展名的文件名
Console.WriteLine(Path.GetFileNameWithoutExtension(path));
//获取文件的扩展名
Console.WriteLine(Path.GetExtension(path));
//获取文件所在的文件夹的名称
Console.WriteLine(Path.GetDirectoryName(path));
//获取文件绝对路径
Console.WriteLine(Path.GetFullPath(path));
//将两个字符串组合成一个路径
Console.WriteLine(Path.Combine(@"C:\a\","b.txt"));
Console.ReadLine();
2、File类
//创建文件,若重复运行则操作的是第一次创建的文件,后面的创建并不是覆盖创建,而是修改文件 /*File.Create(@"C:\Users\kevin\Desktop\file.txt");
Console.WriteLine("创建成功");
Console.ReadLine();*/
//删除一个文件,连回收站也没有
/*File.Delete(@"C:\Users\kevin\Desktop\file.txt");
Console.WriteLine("删除成功");
Console.ReadLine();*/
File.Copy(@"C:\Users\kevin\Desktop\file.txt", @"C:\Users\kevin\Desktop\newfile.txt");
Console.WriteLine("复制成功");
Console.ReadLine();
3、 File 类的读写文件
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace file_study03
{
internal class Program
{
static void Main(string[] args)
{
//File相关方法
//1、读数据(可读取非文本文件,因为所有的东西都是以字节的形式存储的)
/*string path = @"E:\诗词清单.txt";
byte[] b = File.ReadAllBytes(path);
//将字节数组中的每一个元素都要按照指定的编码格式解码成字符串
//Default:ANSI
//string str = Encoding.Default.GetString(b); 出现乱码则说明不是使用ANSI编码,需要查看文件的编码格式
string str = Encoding.GetEncoding("UTF-8").GetString(b);
Console.WriteLine(str);
Console.ReadLine();*/
//2、写数据
//没有这个文件的话会给你创建一个,有的话则会覆盖掉
/*string str = "自在飞花轻似梦 秦观";
//需要字符串转换为字节数组
byte[] b = Encoding.Default.GetBytes(str);
string path = @"E:\诗词清单.txt";
File.WriteAllBytes(path, b);
Console.WriteLine("写入成功");
Console.ReadKey();*/
//以行的形式进行读取(读取文本文件)
/*string[] str = File.ReadAllLines(@"E:\诗词清单.txt",Encoding.Default);
foreach (string s in str)
{
Console.WriteLine(s);
}
Console.ReadLine();*/
//读取文本文件
/*string str = File.ReadAllText(@"E:\诗词清单.txt",Encoding.Default);
Console.WriteLine(str);
Console.ReadLine();*/
//以行的形式写入
/*File.WriteAllLines(@"E:\诗词清单.txt", new string[] { "无边丝雨细如愁", "秦观" });
Console.WriteLine("写入成功");
Console.ReadLine();*/
//以文本形式写入
/*File.WriteAllText(@"E:\诗词清单.txt", "自在飞花轻似梦");
Console.WriteLine("写入成功");
Console.ReadLine();*/
File.AppendAllText(@"E:\诗词清单.txt", "无边丝雨细如愁");
Console.WriteLine("追加成功");
Console.ReadLine();
}
}
}
out、ref、params参数
1、out参数
out
参数可以说是一个接收传递数据的返回值,当我们需要从多个方法中返回多个值,就使用out
参数
out
参数是一种特殊的参数修饰符,它允许方法在返回时修改参数的值
但是注意:out
参数的值必须要在方法内部中去定义
代码演示:
class Program
{
static void Main(string[] args)
{
int result;
Multiply(5, 10, out result);
Console.WriteLine("乘积是:" + result); //50
}
static void Multiply(int a, int b, out int product)
//声明的方式就是在需要接收传递参数前加上out关键字
{
// 使用out参数修改product的值
product = a * b;
}
}
2、ref参数
ref
参数可以说是out
参数的升级版,ref
参数基本使用跟out
参数一样是允许方法去修改传递他们的参数值
与out参数的区别:
初始值要求:ref
参数传递方法的变量在调用方法之前要初始化,但是对于out
参数可以在方法内部再去进行初始化,
方法内部赋值要求:对于ref
参数,你可以在方法内部不为它赋值,但在方法结束之前必须对其赋值;而对于out
参数,你必须在方法内部为它赋值。
代码示例:
class Program
{
static void Main(string[] args)
{
int number = 10;
AddFive(ref number);
Console.WriteLine("修改后的值:" + number); //15
}
static void AddFive(ref int num)
{
// 修改传递进来的参数值
num += 5;
}
}