基本概念
类和结构的重要区别:
- 类类型的对象通过引用传递,结构类型的对象按值传递。
2. 类类型存储在堆(heap),而结构类型存储在栈(stack)。
类中不能添加执行代码!
name="jack"
这样的代码我们称之为“执行代码”,意思就是说这些代码只有在被执行的时候才会有效果。
字段
字段是类的数据成员,它是类型的一个变量,该类型是类的一个成员。
var customer1 = new PjoneCustomer();customer1.FirstName = "Simon";
只读字段
保证对象的字段不能改变,字段可以用readonly修饰声明。
用readonly修饰符声明字段,只允许在构造函数中初始化属性的值。
public class Docunment{private readonly DateTime _creationTime;public Document(){_creationTime = DateTime.Now;}}void SomeMethod(){s_maxDocuments = 10;//compilation error here.MaxDocuments is readonly}
不可变的类型
如果对象没有任何可以改变的成员,只有只读成员,它就是一个不可变的类型。
String类:没有定义任何允许改变其内容的成员。(ToUpper的方法总是返回一个新的字符串,但传递到构造函数的原始字符保持不变)
常量
常量与类相关(没有static修饰符),编译器使用真实值代替常量。
方法
方法的声明
方法修饰符(访问性和返回值类型)+ 方法名 + 输入参数的列表
参数
表达式体方法
如果方法的实现只有一个语句,C#6为方法定义提供了一个简化的语法:表达式体方法。
语法:使用运算符“=>”(lambda操作符)区分左边的声明和操作符右边的实现代码。
namespace Csharp_test01{class Program{static void Main(string[] args){WriteLine($"Pi is {Math.GetPi()}\n");int x = Math.GetSquareOf(5);WriteLine($"Square is {x}\n");var math = new Math();math.Value = 30;WriteLine($"{math.Value}\n");WriteLine($"{math.GetSquare()}\n");}}}public class Math{public int Value { get; set; }public int GetSquare() => Value * Value;public static int GetSquareOf(int x) => x * x;public static double GetPi() => 3.14159;}
方法的重载
方法名相同,但参数的个数或数据类型不同。为了重载方法,只需要声明同名但参数个数或类型不同的方法即可。
/*类型名不同的方法重载*/class ResultDisplayer{public void DisplayResult(string result){//implemention}public void DisplayResult(int result){//implemention}}/*数量不同的方法重载*/class Myclass{public int DoSomething(int x){return DoSomething(x,10);}public int DoSomething(int x,int y){//implementation}}
命名的参数调用
调用方法时,变量名不需要添加到调用中。
语法:“变量名”:“Value”
注意:编译器会去掉变量名,创建一个方法调用,就像没有变量名一样。
例如:
class r{public void MoveAndResize(int x,int y,int width,int height)}MoveAndResize(30,20,20,40);//也可以改变调用,明确数字的含义MoveAndResize(x:30,y:40,width:20,height:40);
可选参数
参数是可选的;
注意:①必须为参数提供默认值;②可选参数必须是方法定义的最后的参数;
③编译器更改调用代码,填充所有的参数,如果以后添加另一个参数,早期的调用程序就会失败。
pubilc void TestMethod(int notOptionNumber,int optionalNumber = 42){Writeline(optionalNumber + notOptionNumber);}TestMethod(11);TestMethod(11,22);
个数可变的参数
有一种语法允许数量可变的参数(这个语法没有版本控制的问题)
public void AnyNumberOfArguments(params int[] data){foreach(var x in data){WriteLine(x);}}/*该函数的参数类型是int[]**可以传递int数组**params关键字:可以传递一个或任何数量的值*/AnyNumberOfArguments(1,2,3,4);
将不同类型的参数传递给方法,可以使用object数组:
public void AnyNumberOfArguments(params objects[] data){//etc}AnyNumberOfArguments("text",43);
注意:“params”关键字如果与其他多个参数一起使用,
① 则params”只能使用一次
② 必须是最后一个参数
属性
属性是可以从客户端访问的函数组,其访问方式与访问类的公共字段类似。
属性的作用
对字段进行赋值时加以限制,因本段代码字段是姓名所以没有限制。
public class Employee{//字段private byte age;//属性public byte Age{get { return age; }set {if(value >= 18 && value<=60)age = value;}}}
属性与字段的区别
这段代码中声明了name字段和Name属性,一般来说属性名是变量名的首字母大写。
public class Employee{//字段private string name;//属性public string Name{get { return name; }set { name = value; }}}
只读属性
在属性定义中省略set访问器,就可以创建只读属性。(一般不建议)
自动实现的只读属性
pubulic string Id{get;} = Guid.NewGuid().ToString();
编译器会创建一个只读字段和一个属性,其get访问器可以访问这个字段。
不可变的类型
具有表达式体的属性访问器
private string _firstName;public string FirstName{get => _firstName;set => _firstName =Value;}//属性访问器的实现只能由一条语句组成
构造函数
构造函数是在实例化对象时自动调用的特殊函数。必须与所属的类同名,且不能有返回类型。用于初始化字段的值。
(1) 如果没有编写构造函数,编译器会生成一个默认构造函数,它会把成员字段初始化为标准的默认值;(例如:引用类型为空引用,数据类型为9,bool为false)
(2) 如果编写了带参数的构造函数,编译器就不会自动提供默认的构造函数;
(3) 构造函数的重载遵循与其他方法相同的规则。
如果将构造函数定义为private或protected,这样不想关的类就不能访问它们。
这样定义有两种情况下回使用:
(1) 类仅用于静态成员或属性的容器,因此永远不会实例化它。
(2) 希望类仅仅通过调用某个静态函数来实例化。
public class MyNumber{private int _number;private MyNumber(int number){}}public class Singleton{private static Singleton s_instance;private int _state;private Singleton(int state){_state = state;}public static Singleton Instance{get { return s_instance ?? (s_instance = new Mysingleton(43)); }}}/** 该类包含一个私有构造函数,只能在类中实例化它本身。* 如果这个字段尚未初始化(null),就调用实例构造函数,创建一个新的实例。*/
从构造函数中调用其他构造函数
l 一个类中可以包含几个构造函数,以容纳某些可选参数;
l 这些构造函数包含共同的代码;
class Car{private string _description;private uint _nWheels;public Car(string description,uint nWheels){_description = description;_nWheels = nWheels;}public Car(string decription):this(decription,4){}}/** this 关键字仅调用参数最匹配的那个构造函数。*/
假定运行下面代码:
var myCar = new Car("xxxxxx");
在本例中,在带一个参数的构造函数的函数体执行之前,先执行两个参数的构造函数。也可以包含对直接基类的构造函数的调用。
静态构造函数
静态构造函数只执行一次,其他的构造函数是实例构造函数,只要创建类的对象就会执行它。
在C#中,通常在第一次调用类的任何成员之前执行静态构造函数。
.NET运行库不能确保静态函数在特定时间执行静态构造函数,所以不能把关键代码放在静态构造函数中。
特点:
(1) 静态构造函数没有访问修饰符;
(2) 静态构造函数不能带任何参数;
(3) 一个类有且只有一个静态构造函数;
(4) 静态构造函数只能访问类的静态成员,不能访问类的实例成员。
用法:
namespace Csharp_test01{class Program{static void Main(string[] args){WriteLine($"{Userpreferences.BackColor}");}}}public enum Color{white,Red,Green,Blue,Black}public static class Userpreferences{public static Color BackColor { get; }static Userpreferences(){DateTime now = DateTime.Now;if(now.DayOfWeek == DayOfWeek.Saturday|| now.DayOfWeek == DayOfWeek.Sunday){BackColor = Color.Green;}else{BackColor = Color.Red;}}}
索引器
运算符
事件
事件是类的成员,在发生某些行为(修改类的字段或属性,进行了某种形式的用户交互操作)时,可以让对象通知调用方。
析构函数
类似于构造函数的语法,当CLR检测到不再需要某个对象时调用它。
类型
嵌套类
说明:嵌套类(nested class)完全封装(嵌套)在另一个类的声明中。
限制:嵌套类提供了一种方便的方法,让外部类能够创建并使用其对象,但在外部类的外面不能访问它们。
嵌套类的访问级别:
嵌套类的访问级别至少与包含它的类相同。例如:
嵌套类为 public,而包含它的类为internal,则嵌套类的访问级别默认也为 internal,只有所属程序集的成员能够访问它。
如果包含它的类为public,嵌套类遵循的访问级别规则将与非嵌套类相同。
分布类
说明:将类声明分成多个部分—通常存储在多个文件中。
分部类的实现方法与常规类完全相同,但在关键字class 前面有关键字partial。
静态类
静态类通常包含实用程序或辅助方法(helper method),它们不需要类实例就能 工作。
不可能创建静态类的实例
静态类只能包含静态成员,但这些成员并不会自动变成静态的,您必须显式地使用修饰符 static。然而,可将任何静态成员声明为 public、 private或internal的。
扩展方法是常规的静态方法,但第一个参数包含修饰符this,
该参数指定要扩展的类型,通常称为类型扩展参数 。
扩展方法:
扩展方法就是在不修改原有类源代码的情况下,添加方法。
为string类型扩展一个ToInt方法:
/// <summary>/// 1、定义一个静态类/// 2、静态类的名称和要实现扩展方法的具体类无关/// </summary>public static class SomeClass{/// <summary>/// 3、实现一个具体的静态方法/// </summary>/// <param name="str">4、第一个参数必须使用this关键字指定要使用扩展方法的类型</param>/// <returns></returns>public static int ToInt(this string str){return int.Parse(str);}}
匿名类型
var关键字,用于表示隐式类型化的变量,var与new关键字一起使用就可以创建匿名类型。
var captain = new {FirstName = "James",MiddleName = "T",LastName = "Kirk"};
结构体
结构是值类型,不是引用类型,存储在栈中或内联。
特点:
(1) 结构不支持继承;
(2) 如果没有提供默认的构造函数,编译器会自动提供一个,把成员初始化为其默认值;
(3) 使用结构,可以指定字段如何在内存中布局。
public struct Dimensions{public double Lengh{get;set;}public double Width{get;set;}public Dimensions(double Lengh,double width){Length = length;Width = width;}public double Diagonal => Math.Sqrt(Length * Length +Width * Width);
结构是值类型
语法上可以当做类处理。
var point = new Dimensions();point.Length = 3;point.Width = 6;
注意:这里的new并不分配堆中的内存,而是只调用相应的构造函数。(进行初始化)
也可以这样编辑,但是一定要初始化数据的value。
Dimensions point;point.Length = 3;point.Width = 6;
结构与继承
结构不能从另一个结构中继承。
唯一例外的是对应的结构最终派生于类System.Object。
每个结构都派生自ValueType。
结构的构造函数
public struct Dimensions{public double Length;public double Width;public Dimensions(double length,double width){Length = length;Width = width;}}
结构与对象的异同
语法上:
(1) 从语法上来看.它们的语法都大同小异,类里面的成员几乎都可以定义在结构体中,但是析构函数除外。
(2) 在结构体中可以声明字段,但是声明字段的时候是不能给初始值的;
(3) 结构体不能包含无参数的构造函数。
(4) 结构体的构造函数可以写任意的代码,在类中不能包含执行代码;
(5) C#语法规定在结构体的构造函数中,必须要为结构体的所有字段赋值。
(6) 在结构体的构造函数中不能为属性赋值,要直接为字段赋值。
