基本概念

类和结构的重要区别:

  1. 类类型的对象通过引用传递,结构类型的对象按值传递。
    2. 类类型存储在堆(heap),而结构类型存储在栈(stack)。

类中不能添加执行代码!

  1. name="jack"

这样的代码我们称之为“执行代码”,意思就是说这些代码只有在被执行的时候才会有效果。

字段

字段是类的数据成员,它是类型的一个变量,该类型是类的一个成员。

  1. var customer1 = new PjoneCustomer();
  2. customer1.FirstName = "Simon";

只读字段

保证对象的字段不能改变,字段可以用readonly修饰声明。
用readonly修饰符声明字段,只允许在构造函数中初始化属性的值。

  1. public class Docunment
  2. {
  3. private readonly DateTime _creationTime;
  4. public Document()
  5. {
  6. _creationTime = DateTime.Now;
  7. }
  8. }
  9. void SomeMethod()
  10. {
  11. s_maxDocuments = 10;//compilation error here.MaxDocuments is readonly
  12. }

不可变的类型

如果对象没有任何可以改变的成员,只有只读成员,它就是一个不可变的类型。
String类:没有定义任何允许改变其内容的成员。(ToUpper的方法总是返回一个新的字符串,但传递到构造函数的原始字符保持不变)

常量

常量与类相关(没有static修饰符),编译器使用真实值代替常量。

方法

方法是与特定类关联的函数。

方法的声明

方法修饰符(访问性和返回值类型)+ 方法名 + 输入参数的列表

参数

每个参数都包括参数的类型名和在方法体中的引用名称。

表达式体方法

如果方法的实现只有一个语句,C#6为方法定义提供了一个简化的语法:表达式体方法。
语法:使用运算符“=>”(lambda操作符)区分左边的声明和操作符右边的实现代码。

  1. namespace Csharp_test01
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. WriteLine($"Pi is {Math.GetPi()}\n");
  8. int x = Math.GetSquareOf(5);
  9. WriteLine($"Square is {x}\n");
  10. var math = new Math();
  11. math.Value = 30;
  12. WriteLine($"{math.Value}\n");
  13. WriteLine($"{math.GetSquare()}\n");
  14. }
  15. }
  16. }
  17. public class Math
  18. {
  19. public int Value { get; set; }
  20. public int GetSquare() => Value * Value;
  21. public static int GetSquareOf(int x) => x * x;
  22. public static double GetPi() => 3.14159;
  23. }

}类 - 图1

方法的重载

方法名相同,但参数的个数或数据类型不同。为了重载方法,只需要声明同名但参数个数或类型不同的方法即可。

  1. /*类型名不同的方法重载*/
  2. class ResultDisplayer
  3. {
  4. public void DisplayResult(string result)
  5. {
  6. //implemention
  7. }
  8. public void DisplayResult(int result)
  9. {
  10. //implemention
  11. }
  12. }
  13. /*数量不同的方法重载*/
  14. class Myclass
  15. {
  16. public int DoSomething(int x)
  17. {
  18. return DoSomething(x,10);
  19. }
  20. public int DoSomething(int x,int y)
  21. {
  22. //implementation
  23. }
  24. }

命名的参数调用

调用方法时,变量名不需要添加到调用中。
语法:“变量名”:“Value”
注意:编译器会去掉变量名,创建一个方法调用,就像没有变量名一样。
例如:

  1. class r
  2. {
  3. public void MoveAndResize(int x,int y,int width,int height)
  4. }
  5. MoveAndResize(30,20,20,40);
  6. //也可以改变调用,明确数字的含义
  7. MoveAndResize(x:30,y:40,width:20,height:40);

可选参数

参数是可选的;
注意:①必须为参数提供默认值;②可选参数必须是方法定义的最后的参数;
③编译器更改调用代码,填充所有的参数,如果以后添加另一个参数,早期的调用程序就会失败。

  1. pubilc void TestMethod(int notOptionNumber,int optionalNumber = 42)
  2. {
  3. Writeline(optionalNumber + notOptionNumber);
  4. }
  5. TestMethod(11);
  6. TestMethod(11,22);


个数可变的参数

有一种语法允许数量可变的参数(这个语法没有版本控制的问题)

  1. public void AnyNumberOfArguments(params int[] data)
  2. {
  3. foreach(var x in data)
  4. {
  5. WriteLine(x);
  6. }
  7. }
  8. /*该函数的参数类型是int[]
  9. **可以传递int数组
  10. **params关键字:可以传递一个或任何数量的值
  11. */
  12. AnyNumberOfArguments(1,2,3,4);


将不同类型的参数传递给方法,可以使用object数组:

  1. public void AnyNumberOfArguments(params objects[] data)
  2. {
  3. //etc
  4. }
  5. AnyNumberOfArguments("text",43);

注意:“params”关键字如果与其他多个参数一起使用,
① 则params”只能使用一次
② 必须是最后一个参数

属性

属性是可以从客户端访问的函数组,其访问方式与访问类的公共字段类似。

属性的作用

对字段进行赋值时加以限制,因本段代码字段是姓名所以没有限制。

  1. public class Employee
  2. {
  3. //字段
  4. private byte age;
  5. //属性
  6. public byte Age
  7. {
  8. get { return age; }
  9. set {
  10. if(value >= 18 && value<=60)
  11. age = value;
  12. }
  13. }
  14. }

属性与字段的区别

这段代码中声明了name字段和Name属性,一般来说属性名是变量名的首字母大写。

  1. public class Employee
  2. {
  3. //字段
  4. private string name;
  5. //属性
  6. public string Name
  7. {
  8. get { return name; }
  9. set { name = value; }
  10. }
  11. }

只读属性

在属性定义中省略set访问器,就可以创建只读属性。(一般不建议)

自动实现的只读属性

  1. pubulic string Id{get;} = Guid.NewGuid().ToString();


编译器会创建一个只读字段和一个属性,其get访问器可以访问这个字段。

不可变的类型

具有表达式体的属性访问器
  1. private string _firstName;
  2. public string FirstName
  3. {
  4. get => _firstName;
  5. set => _firstName =Value;
  6. }
  7. //属性访问器的实现只能由一条语句组成

构造函数

构造函数是在实例化对象时自动调用的特殊函数。必须与所属的类同名,且不能有返回类型。用于初始化字段的值。
(1) 如果没有编写构造函数,编译器会生成一个默认构造函数,它会把成员字段初始化为标准的默认值;(例如:引用类型为空引用,数据类型为9,bool为false)
(2) 如果编写了带参数的构造函数,编译器就不会自动提供默认的构造函数;
(3) 构造函数的重载遵循与其他方法相同的规则。

如果将构造函数定义为private或protected,这样不想关的类就不能访问它们。
这样定义有两种情况下回使用:
(1) 类仅用于静态成员或属性的容器,因此永远不会实例化它。
(2) 希望类仅仅通过调用某个静态函数来实例化。

  1. public class MyNumber
  2. {
  3. private int _number;
  4. private MyNumber(int number)
  5. {
  6. }
  7. }
  8. public class Singleton
  9. {
  10. private static Singleton s_instance;
  11. private int _state;
  12. private Singleton(int state)
  13. {
  14. _state = state;
  15. }
  16. public static Singleton Instance
  17. {
  18. get { return s_instance ?? (s_instance = new Mysingleton(43)); }
  19. }
  20. }
  21. /*
  22. * 该类包含一个私有构造函数,只能在类中实例化它本身。
  23. * 如果这个字段尚未初始化(null),就调用实例构造函数,创建一个新的实例。
  24. */

从构造函数中调用其他构造函数

l 一个类中可以包含几个构造函数,以容纳某些可选参数;
l 这些构造函数包含共同的代码;

  1. class Car
  2. {
  3. private string _description;
  4. private uint _nWheels;
  5. public Car(string description,uint nWheels)
  6. {
  7. _description = description;
  8. _nWheels = nWheels;
  9. }
  10. public Car(string decription):this(decription,4)
  11. {
  12. }
  13. }
  14. /*
  15. * this 关键字仅调用参数最匹配的那个构造函数。
  16. */


假定运行下面代码:

  1. var myCar = new Car("xxxxxx");

在本例中,在带一个参数的构造函数的函数体执行之前,先执行两个参数的构造函数。也可以包含对直接基类的构造函数的调用。

静态构造函数

静态构造函数只执行一次,其他的构造函数是实例构造函数,只要创建类的对象就会执行它。
在C#中,通常在第一次调用类的任何成员之前执行静态构造函数。
.NET运行库不能确保静态函数在特定时间执行静态构造函数,所以不能把关键代码放在静态构造函数中。
特点:
(1) 静态构造函数没有访问修饰符;
(2) 静态构造函数不能带任何参数;
(3) 一个类有且只有一个静态构造函数;
(4) 静态构造函数只能访问类的静态成员,不能访问类的实例成员。
用法:

  1. namespace Csharp_test01
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. WriteLine($"{Userpreferences.BackColor}");
  8. }
  9. }
  10. }
  11. public enum Color
  12. {
  13. white,
  14. Red,
  15. Green,
  16. Blue,
  17. Black
  18. }
  19. public static class Userpreferences
  20. {
  21. public static Color BackColor { get; }
  22. static Userpreferences()
  23. {
  24. DateTime now = DateTime.Now;
  25. if(now.DayOfWeek == DayOfWeek.Saturday
  26. || now.DayOfWeek == DayOfWeek.Sunday)
  27. {
  28. BackColor = Color.Green;
  29. }
  30. else
  31. {
  32. BackColor = Color.Red;
  33. }
  34. }
  35. }

索引器

索引器允许对象用访问数组的方式访问。

运算符

支持运算符重载

事件

事件是类的成员,在发生某些行为(修改类的字段或属性,进行了某种形式的用户交互操作)时,可以让对象通知调用方。

析构函数

类似于构造函数的语法,当CLR检测到不再需要某个对象时调用它。

类型

类可以包含内部类。

嵌套类

说明:嵌套类(nested class)完全封装(嵌套)在另一个类的声明中。
限制:嵌套类提供了一种方便的方法,让外部类能够创建并使用其对象,但在外部类的外面不能访问它们。
嵌套类的访问级别:
嵌套类的访问级别至少与包含它的类相同。例如:
嵌套类为 public,而包含它的类为internal,则嵌套类的访问级别默认也为 internal,只有所属程序集的成员能够访问它。
如果包含它的类为public,嵌套类遵循的访问级别规则将与非嵌套类相同。

分布类

说明:将类声明分成多个部分—通常存储在多个文件中。
分部类的实现方法与常规类完全相同,但在关键字class 前面有关键字partial。

静态类

静态类通常包含实用程序或辅助方法(helper method),它们不需要类实例就能 工作。
不可能创建静态类的实例
静态类只能包含静态成员,但这些成员并不会自动变成静态的,您必须显式地使用修饰符 static。然而,可将任何静态成员声明为 public、 private或internal的。

扩展方法是常规的静态方法,但第一个参数包含修饰符this,
该参数指定要扩展的类型,通常称为类型扩展参数 。
扩展方法:
扩展方法就是在不修改原有类源代码的情况下,添加方法。
为string类型扩展一个ToInt方法:

  1. /// <summary>
  2. /// 1、定义一个静态类
  3. /// 2、静态类的名称和要实现扩展方法的具体类无关
  4. /// </summary>
  5. public static class SomeClass
  6. {
  7. /// <summary>
  8. /// 3、实现一个具体的静态方法
  9. /// </summary>
  10. /// <param name="str">4、第一个参数必须使用this关键字指定要使用扩展方法的类型</param>
  11. /// <returns></returns>
  12. public static int ToInt(this string str)
  13. {
  14. return int.Parse(str);
  15. }
  16. }

匿名类型

var关键字,用于表示隐式类型化的变量,var与new关键字一起使用就可以创建匿名类型。

  1. var captain = new {
  2. FirstName = "James",
  3. MiddleName = "T",
  4. LastName = "Kirk"
  5. };

结构体

结构是值类型,不是引用类型,存储在栈中或内联。
特点:
(1) 结构不支持继承;
(2) 如果没有提供默认的构造函数,编译器会自动提供一个,把成员初始化为其默认值;
(3) 使用结构,可以指定字段如何在内存中布局。

  1. public struct Dimensions
  2. {
  3. public double Lengh{get;set;}
  4. public double Width{get;set;}
  5. public Dimensions(double Lengh,double width)
  6. {
  7. Length = length;
  8. Width = width;
  9. }
  10. public double Diagonal => Math.Sqrt(Length * Length +Width * Width);

}

结构是值类型

语法上可以当做类处理。

  1. var point = new Dimensions();
  2. point.Length = 3;
  3. point.Width = 6;

注意:这里的new并不分配堆中的内存,而是只调用相应的构造函数。(进行初始化)
也可以这样编辑,但是一定要初始化数据的value。

  1. Dimensions point;
  2. point.Length = 3;
  3. point.Width = 6;

结构主要用于小的数据结构。

结构与继承

结构不能从另一个结构中继承。
唯一例外的是对应的结构最终派生于类System.Object。
每个结构都派生自ValueType。

结构的构造函数

  1. public struct Dimensions
  2. {
  3. public double Length;
  4. public double Width;
  5. public Dimensions(double length,double width)
  6. {
  7. Length = length;
  8. Width = width;
  9. }
  10. }

结构与对象的异同

语法上:
(1) 从语法上来看.它们的语法都大同小异,类里面的成员几乎都可以定义在结构体中,但是析构函数除外。
(2) 在结构体中可以声明字段,但是声明字段的时候是不能给初始值的;
(3) 结构体不能包含无参数的构造函数。
(4) 结构体的构造函数可以写任意的代码,在类中不能包含执行代码;
(5) C#语法规定在结构体的构造函数中,必须要为结构体的所有字段赋值。
(6) 在结构体的构造函数中不能为属性赋值,要直接为字段赋值。

按值和按引用传递参数