//控制台输入整数和字符串 int age = int.Parse(Console.ReadLine()); string name = Console.ReadLine(); //控制台输入浮点数 double salary = double.Parse(Console.ReadLine()); double addSalary = Convert.ToDouble(Console.ReadLine()); //控制台格式化输出 Console.WriteLine("这是第一个C#控制台程序"); Console.WriteLine("Your name is {0}, age is {1}, and salary is {2:f2}, addSalary is {3}", name, age, salary, addSalary.ToString()); //格式化到字符串 int age = 32; double salary = 12000.59; String name = "zhangsan"; String outstr = String.Format("name={0}, age={1}, salary={2:f1}", name, age, salary); Console.WriteLine(outstr); //输出日期和时间,DateTime.Today只获取日期 Console.WriteLine("{0:D} {0:t}", DateTime.Now); 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
C#中的数据类型包含值类型(int,double,float,string等),引用类型(object,dynamic和string三种内置的引用类型,以及class等)和指针类型(C#中不建议使用指针类型)。数组
**如何区别值类型和引用类型? **
值类型:能够直接赋值的一般是值类型,好比int,float等,String类型除外,它是特殊的引用类型;
引用类型:一般是对象,好比从Object派生的对象,能够设置为null,还须要使用new操做符来申请内存空间构造对象;好比本身实现的class,系统定义的class等数据结构
类型检查
sizeof()判断类型的大小,判断值类型有效,因为在C#中引用类型相似C++中的引用,不存储内存空间,所以不能用sizeof()计算引用类型的空间大小;
typeof()返回数据的类型app
内置的引用类型函数
//对象(Object)类型,装箱就是这里将值类型转换为引用类型,反之就是拆箱 object obj1 = 100; object obj2 = 100.5; Console.WriteLine("obj1 is {0}, obj2 is {1}", obj1.GetType().ToString(), obj2.GetType().ToString()); //动态(Dynamic)类型,能够是值类型,也能够是引用自定义class类型 dynamic obj3 = 100; dynamic obj4 = 100.5; Console.WriteLine("obj3 is {0}, obj4 is {1}", obj3.GetType().ToString(), obj4.GetType().ToString()); //字符串类型,使用@将转义字符(\)看成普通字符对待 string str1 = @"C:\Windows"; string str2 = "C:\\Windows"; if (str1 == str2) { Console.WriteLine("他们相等"); }
动态类型与对象类型类似,可是对象类型变量的类型检查是在编译时发生的,而动态类型变量的类型检查是在运行时发生的。指针
用户自定义引用类型
用户或系统实现的class、interface 或 delegate等;日志
显示和隐式转换
等同于C++的操做code
int v = 10; double d = v; //隐式转换 int v2 = (int)d; //显式转换
AS操做(引用类型转换)orm
//子类到基类的转换,两种方法均可以 //第一种是强制转换,在编译期间会进行判断 //第二种转换在失败时base b会为空,不抛出异常 base b = subclass; base b = subclass as base; //基类到派生类的转换: //C++:subclass* sub = dynamic_cast<base>(base); //if (sub != nullptr) 须要作一下判断是否转换成功 Rectangle rect2 = sh as Rectangle; if (rect2 != null) { //须要作一下判断,转换失败rect2为空,但不会抛出异常 }
使用AS操做符转换,可是AS只能用于引用类型和可为空的类型。使用as有不少好处,当没法进行类型转换时,会将对象赋值为NULL,避免类型转换时报错或是出异常。C#抛出异常在进行捕获异常并进行处理是很消耗资源的,若是只是将对象赋值为NULL的话是几乎不消耗资源的(消耗很小的资源)。对象
装箱和拆箱(值类型和引用类型转换)继承
object obj1 = 100; object obj2 = 100.5; Console.WriteLine("obj1 is {0}, obj2 is {1}", obj1.GetType().ToString(), obj2.GetType().ToString());
装箱和拆箱在值类型和引用类型之间架起了一座桥梁,使得任何 value-type 的值均可以转换为 object 类型的值,反过来转换也能够。 装箱:装箱是指将一个值类型的数据隐式地转换成一个对象类型(object)的数据。执行装箱操做时不可避免的要在堆上申请内存空间,并将堆栈上的值类型数据复制到申请的堆内存空间上,这确定是要消耗内存和cpu资源的。注意:在执行装箱转换时,也可使用显式转换。 拆箱:拆箱是指将一个对象类型的数据显式地转换成一个值类型数据。拆箱过程是装箱的逆过程,是将存储在堆上的引用类型值转换为值类型并赋给值类型变量。拆箱操做分为两步:一是检查对象实例,确保它是给定值类型的一个装箱值;而是将该值从实例复制到值类型变量中。装箱和拆箱都是要消耗内存和cpu资源的,也就形成效率下降,因此要尽可能避免使用。
//num1不能为空值,可是可使用?符号来定义可空值类型 double? num1 = null; double? num2 = 3.14157; double num3; // num1 若是为空值则返回 5.34 num3 = num1 ?? 5.34; Console.WriteLine("num3 的值: {0}", num3); num3 = num2 ?? 5.34; Console.WriteLine("num3 的值: {0}", num3); Console.ReadLine();
//声明数组 int[] age; double[] balance; //初始化数组并赋值 age = new int[5] { 1, 2, 3, 4, 5 }; //也能够不指定长度:age = new int[] { 1, 2, 3, 4, 5 }; //初始化数组使用默认值0 balance = new double[10]; //访问数组元素 int val = age[3]; //遍历数组元素 foreach (int v in age) { Console.WriteLine("{0}", v); }
//声明数组 int[,] age; double[,] balance; //初始化数组并赋值 age = new int[2,3] { { 1, 2, 3 }, { 4, 5, 6 } }; //也能够不指定长度:age = new int[,] { { 1, 2, 3 }, { 4, 5, 6 } }; //初始化数组使用默认值0 balance = new double[5, 10]; //访问数组元素 int val = age[0,1]; //遍历数组元素 foreach (int v in age) { Console.WriteLine("{0}", v); }
交错数组是数组的数组,不一样于多维数组。多维数组本质上元素都是一个数据类型,而交错数组的元素本质上是另一个数组。交错数组和C++的多维数组很相似,声明和使用看起来都同样,要特别注意!
//声明数组 int[][] scores; //初始化数组 scores = new int[5][]; for (int i = 0; i < scores.Length; i++) { //数组元素是一个有4个元素的数组 scores[i] = new int[4] { i * i + 1, i * i + 2, i * i + 3, i * i + 4 }; } //也能够直接初始化:scores = new int[2][] { new int[] { 92, 93, 94 }, new int[] { 85, 66, 87, 88 } }; //访问数组元素 int val = scores[0][2]; //遍历数组元素 for (int i = 0; i < 5; i++) { for (int j = 0; j < 4; j++) { Console.WriteLine("a[{0}][{1}] = {2}", i, j, scores[i][j]); } }
Array 类是 C# 中全部数组的基类,它是在 System 命名空间中定义。
Array 类提供了各类用于数组的属性和方法。
Array类是一个抽象类,不能被实例化。
struct Books { public string title; public string author; }; Books b; b.title = "Eleven"; b.author = "ZhangSan"; Console.WriteLine("{0}, {1}", b.title, b.author);
在 C# 中,结构体是值类型数据结构。它使得一个单一变量能够存储各类数据类型的相关数据。
C# 中的结构有如下特色:
类和结构有如下几个基本的不一样点:
C# 枚举是值类型。换句话说,枚举包含本身的值,且不能继承或传递继承。
//默认从0开始,依次加1 enum Days { Sun, Mon, tue, Wed, thu, Fri, Sat }; //指定从10开始,中间Fri指定为20,Sat就是21 enum Days2 { Sun = 10, Mon, tue, Wed, thu, Fri=20, Sat }; //和C++不同,这里会打印字符串Sun,Fri,Sat Console.WriteLine("{0}, {1}, {2}", Days.Sun, Days.Fri, Days.Sat); Console.WriteLine("{0}, {1}, {2}", Days2.Sun, Days2.Fri, Days2.Sat); //强制将枚举转换为int,才会获得和C++同样的结果 Console.WriteLine("{0}, {1}, {2}", (int)Days.Sun, (int)Days.Fri, (int)Days.Sat); Console.WriteLine("{0}, {1}, {2}", (int)Days2.Sun, (int)Days2.Fri, (int)Days2.Sat);
和C++同样,C#也有值传递和引用传递,可是比C++多一个参数传出功能。
//引用传递使用ref而不是& public void swap(ref int x, ref int y) { int temp = x; x = y; y = temp; } //输出参数传递,对于未初始化的参数获取颇有用 public void GetValues(out int a, out int b) { Console.WriteLine("请输入第一个值: "); a = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("请输入第二个值: "); b = Convert.ToInt32(Console.ReadLine()); } int x = 100; int y = 200; MyClass n = new MyClass(); n.swap(ref x, ref y); Console.WriteLine("在交换以后,x 的值: {0}", x); Console.WriteLine("在交换以后,y 的值: {0}", y); int a; int b; n.GetValues(out a, out b); Console.WriteLine("a={0}, b={1}", a, b); //这里不能使用swap,由于a,b都没有初始化 //n.swap(ref a, ref b);
C#的引用传递可使用值传递也能够显式使用引用传递,效果是同样的。
C#将Object obj2 = obj1当成对象引用,而不是建立新的对象,建立新对象都使用new操做符。而C++对象引用必须强制使用&符号,好比:Object& obj2 = obj1。这点和C++有很大区别!
class MyData { public int val = 0; double v2 = 100.3232; //经过传递引用改变数据 static public void ChangeValue1(ref MyData d) { d.val = 60; } //经过传递值改变数据,由于这个值是一个引用类型,所以能够改变数据 //若是参数是值类型,如int,float等就不能够 static public void ChangeValue2(MyData d) { d.val = 100; } } //使用new建立对象 MyData v = new MyData(); MyData.ChangeValue1(ref v); Console.WriteLine("v.val={0}", v.val); MyData.ChangeValue2(v); Console.WriteLine("new v.val={0}", v.val);
C#数组也是对象,所以参数传递中的数组等同于引用传递:
//也能够static void init(ref int[] array)这样声明 static void init(int[] array) { int index = 0; int size = array.Length; for (int i=0; i<size; i++) { array[i] = ++index; } } //建立一个10个元素长度的数组 int[] array = new int[10]; //使用参数传递初始化数组,等同于Program.init(ref array); Program.init(array); //打印数组元素 foreach (int i in array) { Console.Write("{0} ", i); }
相似于C/C++的可变参数(不定长参数),C# 经过使用参数数组来实现不可知个数的参数传递。
1.带 params 关键字的参数类型必须是一维数组,不能使用在多维数组上;
2.不容许和 ref、out 同时使用;
3.带 params 关键字的参数必须是最后一个参数,而且在方法声明中只容许一个 params 关键字。
4.不能仅使用 params 来使用重载方法。
5.没有 params 关键字的方法的优先级高于带有params关键字的方法的优先级
//参数数组只能传递int类型 public static void UseParams(params int[] list) { for (int i = 0; i < list.Length; i++) { Console.Write(list[i] + " "); } Console.WriteLine(); } //能够传递任意object类型 public static void UseParams2(params object[] list) { for (int i = 0; i < list.Length; i++) { Console.Write(list[i] + " "); } Console.WriteLine(); } UseParams(1, 2, 3, 4); UseParams2(1, 'a', "Apple"); UseParams2(); //不填入参数 //和UseParams(1, 2, 3, 4);等价 int[] myIntArray = { 5, 6, 7, 8, 9, }; UseParams(myIntArray); object[] myObjArray = { 1, 'b', "boom", "app" }; UseParams2(myObjArray); //输出为"System.Int32[]",这里把数组看做为一个object类型了 UseParams2(myIntArray);
静态常量
所谓静态常量就是在编译期间会对变量进行解析,再将常量的值替换成初始化的值。
动态常量
所谓动态常量就是编译期间会将变量标记只读常量,而不用常量的值代替,这样在声明时能够不初始化,能够延迟到构造函数初始化。
const修饰的常量是上述中的第一种,即静态常量,而readonly是上述中第二种即动态常量。他们的区别能够从静态常量和动态常量的特性来讲明:
和C++同样,C#也能够定义一系列预处理指令。能够在项目属性中设置预处理指令:
勾选DEBUG和TRACE常量会定义这两个预约义指令,在条件编译符号中还能够自行定义须要的预处理指令,用空格分开。
另外还能够在代码文件的using指令前定义预处理指令:
#define GPU using System; ... #if GPU Console.WriteLine("Use GPU"); #else #warning Only Support GPU ! #endif
这里使用#warning在编译时产生一个编译警告(还能够#error产生编译错误)。
另外比较经常使用的就是代码段管理预处理指令,在IDE中能够折叠被#region包围的代码:
#region 私有成员 private int _width = 0; private int _height = 0; #endregion #region 公有方法 public void Submit() { } public static void Add(MyClass A, MyClass B) { } #endregion
这样一来就能够折叠这段代码,若是没有显示+号,就须要在菜单->编辑->大纲显示,开启代码的大纲显示功能。
C#提供了很丰富的异常处理机制,这点和C++很类似,连关键字try,catch,finally都是如出一辙。整体上分为两类异常:SystemException和ApplicationException。
下面的代码展现自定义异常类的最多见用法:
public class MyClass { public int Work(int a, int b) { if (b == 0) { throw new MyException("除数不能为零!"); } return a / b; } } //自定义异常处理类,从ApplicationException派生 public class MyException : ApplicationException { public MyException(string messag) : base(messag) { } } try { MyClass my = new MyClass(); int value = my.Work(100, 0); Console.WriteLine("value is {0}", value); } catch (MyException e) { //捕获自定义异常 Console.WriteLine("MyException : {0}", e.Message); } catch (Exception e) { Console.WriteLine("Exception : {0}", e.Message); //若是不想处理,仍然能够继续抛出异常 throw e; } finally { //不管是否抛出异常都会执行这段代码 Console.WriteLine("程序退出"); }
一、通用文件流FileStream操做,便可写文件也可读取文件
//打开文件流 FileStream F = new FileStream("test.dat", FileMode.OpenOrCreate, FileAccess.ReadWrite); //写文件数据 for (int i = 1; i <= 20; i++) { F.WriteByte((byte)i); } //读取文件流 F.Position = 0; for (int i = 0; i <= 20; i++) { Console.Write(F.ReadByte() + " "); } //关闭文件 F.Close();
二、简单文本文件的写入StreamWriter和读取StreamReader
//写文本文件 string[] names = new string[] { "Zara Ali", "Nuha Ali" }; //使用using也能够关闭文件 using (StreamWriter sw = new StreamWriter("names.txt")) { foreach (string s in names) { sw.WriteLine(s); } } //读取文本文件 string line = ""; using (StreamReader sr = new StreamReader("names.txt")) { while ((line = sr.ReadLine()) != null) { Console.WriteLine(line); } }
三、二进制文件的读BinaryReader和写BinaryWriter
try { //从控制台输入数据并写入二进制文件 BinaryWriter bw = new BinaryWriter(new FileStream("mydata", FileMode.Create)); Int32 vInt = Convert.ToInt32(Console.ReadLine()); Double vDoub = Convert.ToDouble(Console.ReadLine()); String vStr = Console.ReadLine(); bw.Write(vInt); bw.Write(vDoub); bw.Write(vStr); bw.Close(); } catch (IOException e) { Console.WriteLine(e.Message); return; } catch (Exception e) { Console.WriteLine(e.Message); return; } try { // 读取二进制文件 BinaryReader br = new BinaryReader(new FileStream("mydata", FileMode.Open)); Console.WriteLine("Read:\n{0}\n{1}\n{2}", br.ReadInt32(), br.ReadDouble(), br.ReadString()); br.Close(); } catch (IOException e) { Console.WriteLine(e.Message); } catch (Exception e) { Console.WriteLine(e.Message); }
DirectoryInfo 类
DirectoryInfo 类派生自 FileSystemInfo 类。它提供了各类用于建立、移动、浏览目录和子目录的方法。该类不能被继承。
**FileInfo 类 **
FileInfo 类派生自 FileSystemInfo 类。它提供了用于建立、复制、删除、移动、打开文件的属性和方法,且有助于 FileStream 对象的建立。该类不能被继承。
下面是一个自动删除过时日志的函数,使用了递归遍历所有文件:
void DeleteLog(string folderFullName, int preDays = 14) { DateTime timeUp = DateTime.UtcNow.AddDays(-preDays); Console.WriteLine("Will delete old log befor {0}===>", timeUp.ToString()); DirectoryInfo TheFolder = new DirectoryInfo(folderFullName); foreach (DirectoryInfo NextFolder in TheFolder.GetDirectories()) { //遍历子文件夹 DeleteLog(NextFolder.FullName); } foreach (FileInfo fileName in TheFolder.GetFiles()) { //删除指定时间以前的文件 if (DateTime.Compare(timeUp, fileName.LastWriteTime) >= 0) { Console.WriteLine(fileName.FullName); File.Delete(fileName.FullName); } } }