一、第一个程序Hellow Word
using System; //using 关键字用于在程序中包含 System 命名空间。 一个程序一般有多个 using 语句。 using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Helloword { class Program //声明一个类 { static void Main(string[] args) // Main 方法,是所有 C# 程序的 入口点。Main 方法说明当执行时 类将做什么动作。 { Console.WriteLine("Hello Word!"); Console.ReadKey(); } } }
二、C#数据类型
- 值类型(Value types)
- 引用类型(Reference types)
- 指针类型(Pointer types)
(一)值类型
就是那些 float,char,int啊之类的数据类型,具体的类型:
(二)引用类型
引用类型主要包含三种:动态类型,字符串类型和对象类型
引用类型的变量持有的是数据的引用,数据存储在数据堆,分配在托管堆中,变量并不会在创建它们的方法结束时释放内存,它们所占用的内存会被CLR中的垃圾回收机制释放。
1.动态(dynamic)类型
存储任何类型的值在动态数据类型变量中。这些变量的类型检查是在运行时发生的。也就是我可以定义一个动态类型的变量,然后只有执行的时候我才知道这个定义的变量是什么类型的。
什么时候用到这个类型呢?
在Coding中有时候会遇到一些需要解析的数据,可是数据的字段数量和名称未统一,我们没法定义实体类来对应。那么我们就会想到通过C#的dynamic动态类来实现,如果大家注意的话一些ORM框架里面貌似都有用到dynamic来实现一部分功能。
动态类型的基本应用:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 数据类型 { class Program { static void Main(string[] args) { //引用类型 dynamic x =20;
//获取x的数据类型
Type ty = x.GetType(); dynamic y = "name"; Type ty1 = y.GetType();
//打印出上边未知的数据类型 Console.WriteLine(ty.ToString()); Console.WriteLine(ty1.ToString()); Console.ReadKey(); } } }
想了解更多关于dynamic类型:http://www.cnblogs.com/FourLeafCloverZc/p/4348722.html
2.对象(object)类型
对象(Object)类型 是 C# 通用类型系统(Common Type System - CTS)中所有数据类型的终极基类。可以被分配任何其他类型(值类型、引用类型、预定义类型或用户自定义类型)的值。但是,在分配值之前,需要先进行类型转换。
这种类型的转换就是常说的拆箱和装箱。
当是值类型转化成对象类型的时候就是装箱,当对象类型转化成值类型就是拆箱。装箱是隐式的;拆箱必定是显式的。
1.装箱
object ob; obj = 111;
2.拆箱
object obj = 123; int x = int(obj);
为什么要关注装箱和拆箱呢?装箱是需要占用资源的(cpu,内存等等),而在日常编码中,经常有装箱与拆箱的操作,而且这些装箱与拆箱的操作往往是在不经意时发生,装箱更加的占用资源,这不难理解,因为引用对象的分配更加复杂,成本也更高,值类型分配在栈上,分配和释放的效率都很高。装箱过程是需要创建一个新的引用类型对象实例,拆箱过程需要创建一个值类型字段,开销更低。为了尽量避免这种性能损失,尽量使用泛型,在代码编写中也尽量避免隐式装箱。
PS:如果想了解更多拆箱,装箱:http://www.cnblogs.com/anding/p/5236739.html
3.字符串(String)类型
string firstCode = "Hellow Word!";
在C#中,string 是 System.String 的别名,所以基本上在使用时是没有差别的。习惯上,我们把字符串当作对象时(有值的对象实体),我们用string。而我们把它当类时(需要字符串类中定义的方法),我们用String,
比如:string greet = String.Format("Hello {0}!", place);其实乱用也可以,只是这样概念上清楚一点。另外string是C#保留字,不可用作变量名,String就不是了。如果深入详解,String是.net framework平台中通用类型(CTS)的数据类型,而string是开发环境vs从String
类型映射过来的数据类型,无论是他们之中的他一个,在编译后转换成的中间语言(IL)都将是一样的.
三、类和结构
(一) 访问修饰类型
public | 公有访问。不受任何限制。 |
private | 私有访问。只限于本类成员访问,子类,实例都不能访问。 |
protected | 保护访问。只限于本类和子类访问,实例不能访问。 |
internal | 内部访问。只限于本项目内访问,其他不能访问。 |
protected internal | 内部保护访问。只限于本项目或是子类访问,其他不能访问 |
(二)
- 类
类和结构是创建对象的模板,类定义了每个类对象包含的数据和功能。比如说一个顾客表示一个类。那么这个类里面可以包含顾客的姓名,年龄,身高等信息。我们可以实例化这个类,用来表示这个顾客。
Class CustomersClass{ public const string activDay= "Friday"; public int CustomerID; public int age; public string Name; }
- 结构
结构与类的写法相似,struct 关键字用于创建结构体:
struct CustomersStruct{ public const string activDay= "Friday"; public int CustomerID; public int age; public string Name; }
类和结构的区别:
- 类是引用类型,类是存储在堆中的;结构是值类型,存储在栈中。
- 结构不支持继承
- 较小的数据类型使用结构可以提高性能。
- 结构不能声明默认的构造函数。
- 结构体中声明的字段无法赋予初值,类可以
- 结构体的构造函数中,必须为结构体所有字段赋值,类的构造函数无此限制:
结构体和类应用场景:
- 当堆栈的空间很有限,且有大量的逻辑对象时,创建类要比创建结构好一些;
- 对于点、矩形和颜色这样的轻量对象,假如要声明一个含有许多个颜色对象的数组,则CLR需要为每个对象分配内存,在这种情况下,使用结构的成本较低;
- 在表现抽象和多级别的对象层次时,类是最好的选择,因为结构不支持继承。
- 大多数情况下,目标类型只是含有一些数据,或者以数据为主。
类和结构实例化:
CustomersClass customers1 = new CustomersClass(); CustomersStruct customers2 = new CustomersStruct();
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 访问修饰符 { class Customers { public const int birth = 22; public int CustomerID; public string name; public float height; //public 可以在对类进行实例化之后进行调用 public int Buy(int money, int number) { return money * number; } //private 表示的是该方法只能在类内使用,无法在另一个类当中调用,否则就会出现错误 private String action() { return "测试函数"; } } //可以将struct换成是class struct Customers1 { public const int birth = 22; public int CustomerID; public string name; public float height; int age; public int AGE() {
//age 无法在类外使用但是可以在类内使用,在这里对私有成员 age进行赋值,通过公有函数返回值可以通过实例化获取。 age = 56; return age; } } class Program { static void Main(string[] args) { //实例化 Customers1 c1 = new Customers1(); //由于Customers1是私有属性,所以无法访问类和类中的成员 c1.CustomerID = 1; c1.name = "小明"; c1.height = 165.8F; // c1.age = 30; //类成员默认是私有的,只能类的内部使用,类外无法获取,会报错误 Console.WriteLine(c1.AGE()); //age 无法在类外使用但是可以在类内使用 Customers c = new Customers(); c.CustomerID = 2; c.name = "梦"; c.height = 156.8F; Console.WriteLine(c1.CustomerID); Console.WriteLine(c.CustomerID); Console.WriteLine("请输入价格:"); int money =Convert.ToInt32(Console.ReadLine()); Console.WriteLine("请输入数量:"); int number = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("总价是:{0}", c.Buy(money, number)); Console.ReadKey(); // c.action(); //这里会显示错误“不可访问,因为他受到保护级别限制” } } }
四、类成员
类的成员包含数据成员和函数成员。
class Customers { //数据成员 public const int birth = 22; public int CustomerID; public string name; public float height; //函数成员,函数成员包括:方法,属性,构造函数,终结器,索引器,运算符 //public 可以在对类进行实例化之后进行调用 public int Buy(int money, int number) { return money * number; } //private 表示的是该方法只能在类内使用,无法在另一个类当中调用,否则就会出现错误 private String action() { return "测试函数"; } }
(一)方法
1.方法声明
修饰词 返回类型 方法名(参数列表){
//方法体;
}
例如:
//有返回值
public int Buy(int money, int number){
//返回值
return money* number;
}
//没有返回值
public void Buy(){
//返回值
int x = 15;
}
2.方法的调用
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 访问修饰符 { class Customers { public const int birth = 22; public int CustomerID; public string name; public float height; //public 可以在对类进行实例化之后进行调用 public int Buy(int money, int number) { return money * number; } public static int Buy1(int money, int number){
return money * number;
}
}class Program { static void Main(string[] args) { Customers c = new Customers(); c.CustomerID = 2; c.name = "梦"; c.height = 156.8F; Console.WriteLine("请输入价格:"); int money =Convert.ToInt32(Console.ReadLine()); Console.WriteLine("请输入数量:"); int number = Convert.ToInt32(Console.ReadLine()); Console.WriteLine("总价是1:{0}", c.Buy(money, number)); //没有使用static修饰方法,可以使用实例化名称来调用
Console.WriteLine("总价是2:{0}",Customers.Buy1(money, number)); //使用static修饰方法之后,需要用类名来调用,不能使用实例化名称调用 Console.ReadKey(); // c.action(); //这里会显示错误“不可访问,因为他受到保护级别限制” } } }
3.方法传参
在成员方法中几乎所有的参数都是通过值传递,除非特别声明
值传递和引用传递:
- 值传递
方法调用时,实际参数把它的值传递给对应的形式参数,方法执行中形式参数值的改变不影响实际参数的值。
- 引用传递
也称为传地址。方法调用时,实际参数的引用(地址,而不是参数的值)被传递给方法中相对应的形式参数,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,方法执行中形式参数值的改变将会影响实际参数的值。
//这里交换的只是将i和j的值用副本传递给了change,i和j本身没有变化,所以值并没有发生变化 public class TestValue { public static void main(String[] args) { int i = 3; int j = 4; change(i,j); System.out.println(i); System.out.println(j); } public static void change(int i,int j){ int temp = i; i = j; j = temp; } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 值传递和引用传递1 { class Student { private float score; public Student(float score) { this.score = score; } public void setScore(float score) { this.score = score; } public float getScore() { return score; } public static void swap(Student x, Student y) { Student temp = x; x = y; y = temp; } } public class ParamTest { static void Main(string[] args) { Student a = new Student(0); Student b = new Student(100); Console.WriteLine("交换前:"); Console.WriteLine("a的分数:" + a.getScore() + "--- b的分数:" + b.getScore()); Student.swap(a, b); Console.WriteLine("交换后:"); Console.WriteLine("a的分数:" + a.getScore() + "--- b的分数:" + b.getScore()); Console.ReadLine(); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 值传递和引用传递2 { class aa { public static void change(int[] count) {
//这里修改的是地址里面的值,所以可以修改成功 count[0] = 15; } } class Program { static void Main(string[] args) { int[] count = { 1, 2, 3, 4, 5 }; aa.change(count); Console.WriteLine(count[0]); Console.ReadKey(); } } }
4.ref函数
- 方法定义和调用方法都必须显式使用 ref 关键字
- 传递到 ref 参数的参数必须初始化,否则程序会报错
- 通过ref的这个特性,一定程度上解决了C#中的函数只能有一个返回值的问题
值传递是默认的,但是也可以迫使他变成引用传递:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 值传递和引用传递2 { class aa { public static void change(int[] count, ref int i) { count[0] = 15; i = 100; } } class Program { static void Main(string[] args) { int[] count = { 1, 2, 3, 4, 5 }; int i = 10; aa.change(count,ref i); Console.WriteLine(count[0]); Console.WriteLine(i); Console.ReadKey(); } } }
5.out函数
- 方法定义和调用方法都必须显式使用 out关键字
- out参数的参数值初始化必须在其方法内进行,否则程序会报错
- 通过out的这个特性,一定程度上解决了C#中的函数只能有一个返回值的问题
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { int a = 100; int b; Fun(out a, out b); Console.WriteLine("a:{0},b:{1}", a, b);//输出:3和1说明out参数传递进去的是a和b的引用,输出3说明a的参数值没有传入Fun方法中 Console.ReadKey(); } static void Fun(out int a, out int b) { a = 1 + 2; b = 2; } } }
ref和out的区别:
通过上面的解析,ref和out最主要的区别是:
ref将参数的参数值和引用都传入方法中,所以ref的参数的初始化必须在方法外部进行,也就是ref的参数必须有初始化值,否则程序会报错
out不会将参数的参数值传入方法中,只会将参数的引用传入方法中,所以参数的初始化工作必须在其对用方法中进行,否则程序会报错
ref和out的注意:
- 尽管 ref 和 out 在运行时的处理方式不同,但在编译时的处理方式相同。因此,如果一个方法采用 ref 参数,而另一个方法采用 out 参数,则无法重载这两个方法
- 如果一个方法采用 ref 或 out 参数,而另一个方法不采用这两个参数,则可以进行重载
5.函数的重载
方法名相同,但是参数个数和类型不同
1.方法重载:是指在一个类中定义多个同名的方法,但要求每个方法具有不同的参数的类型或参数的个数
2.具体规范:
(1)方法名一定要相同。
(2)方法的参数表必须不同,包括参数的类型或个数,以此区分不同的方法体。
- a.如果参数个数不同,就不管它的参数类型了。
- b.如果参数个数相同,那么参数的类型必须不同。
(3)方法的返回类型、修饰符可以相同,也可不同。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 函数的重载 { class Program { public static int AddNumber(int num1,int num2) { return num1 + num2; } public static double AddNumber(int num1, int num2,int num3) { return num1 + num2+ num3; } public static double AddNumber(double num1, int num2) { return num1 + num2; } static void Main(string[] args) { Console.WriteLine(AddNumber(1, 2)); Console.WriteLine(AddNumber(1, 4)); Console.WriteLine(AddNumber(1.3f, 3)); Console.ReadKey(); } } }
(二)属性
在C#中我们可以很自由的访问共有字段,但有时我们可能需要某字段只能读或者写,或在改变字段值得时候做一些其他事情,显然这些仅仅依靠字段是无法实现的,于是便有了属性。
c#中的属性由属性作用域中定义的get作用域(访问方法)和set作用域(访问方法)构成。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 成员函数之属性 { class Program { static void Main(string[] args) { Test test = new Test(); Console.WriteLine(test.Num); //SavingAccount sa = new SavingAccount(); Console.WriteLine(Customers.Name); Console.ReadKey(); } class Test { private int number; //int表示该属性所封装的数据类型 //数据类型必须与相应的字段(number)相同 public int Num { get { return number; } set { number = value; } } } class Customers { public int age; //静态数据点 private static string name = "aaa"; //静态属性 public static string Name { get { return name; } set { name = value; } } } } }
直接在代码中使用封装了数字或布尔数据的自动属性,因为隐藏的返回字段将设置一个可以直接使用的安全的默认值。但如果自动属性封装了另一个类变量,隐藏的私有引用类型的默认值将设为null。
例如下面的类spa,使用了两个自动属性
class Spa { //隐藏的int字段默认值为0 public int numbers{get;set;} //隐藏的Enemy返回字段为null public Enemy MyEnemy{get;set;} }
(三)构造函数
- 构造函数的名字与类名相同。
- 使用 new 表达式创建类的对象或者结构(例如int)时,会调用其构造函数。并且通常初始化新对象的数据成员。
- 除非类是静态的,否则会为没有构造函数的类,自动生成一个默认构造函数,并使用默认值来初始化对象字段。
- 构造函数可以有参数,可以以多态的形式存在多个构造函数。
- 如果存在有参构造函数,那么系统就不会调用默认的。当没有任何构造函数时,才会调用默认的
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 成员函数之属性之关于自动属性和默认值 { class Customers { public string name { get; set; } public int age { get; set; } public int ID { get; set; } //构造函数:与类名相同的函数并且他的没有返回值类型 public Customers() { //初始化 this.name = name; this.age = age; this.ID = ID; } public Customers(string name, int age) { this.name = name; this.age = age; } } class Program { static void Main(string[] args) { Customers customer = new Customers("dfdsa",3); Console.WriteLine(customer.name); Console.ReadKey(); } } }
(1)静态构造函数
-
静态构造函数不使用访问修饰符或不具有参数。
-
在创建第一个实例或引用任何静态成员之前,将自动调用静态构造函数以初始化类。
-
不能直接调用静态构造函数。
-
用户无法控制在程序中执行静态构造函数的时间。
-
静态构造函数的一种典型用法是在类使用日志文件且将构造函数用于将条目写入到此文件中时使用。
-
静态构造函数对于创建非托管代码的包装类也非常有用,这种情况下构造函数可调用
LoadLibrary
方法。 -
如果静态构造函数引发异常,运行时将不会再次调用该函数,并且类型在程序运行所在的应用程序域的生存期内将保持未初始化。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 构造函数之静态构造函数 { class customers { //静态成员变量 public static int age = 0; //构造函数 public customers() { age = 22; } //静态构造函数 static customers() { age = 100; } } class Program { //第一步,程序入口Main最先执行。然后执行public static int x = 0 接着执行静态构造函数。 static void Main(string[] args) { Console.WriteLine("x:{0}", customers.age); //打印,age = 100 //实例化的时候访问静态变量=0; 然后访问静态构造函数=100; 最后访问构造函数=22 customers Test = new customers(); Console.WriteLine("x:{0}", customers.age); //打印age = 22 Console.ReadKey(); } } }
由上面的程序可以知道了静态构造函数的执行顺序:
1、静态变量 > 静态构造函数 > 静态函数
2、静态变量 > 静态构造函数 > 构造函数