zoukankan      html  css  js  c++  java
  • C#对象和类型——C#高级编程第三章

      本节内容:类和结构的区别、类成员、按值和按引用传送参数、方法重载、构造函数和静态构造函数、只读字段、部分类、静态类、Object类(基类)。

    一、类和结构

    类用class修饰,结构用struct修饰。如:

     1 class PhoneCustomer
    2 {
    3 public const string DayOfSendingBill="Monday";
    4 public int CustomerID;
    5 public string FirstName;
    6 public string LastName;
    7 }
    8
    9 struct PhoneCustomerStruct
    10 {
    11 public const string DayOfSendingBill="Monday";
    12 public int CustomerID;
    13 public string FirstName;
    14 public string LastName;
    15 }

      结构和类的区别是它们在内存中的存储方式、访问方式(类是存储在堆上的引用类型,而结构是存储在栈上的值类型)和它们的一些特征(如结构不支持继承)。较小的数据类型使用结构可以提高性能。在语法上,两者十分相似。对于类和结构都使用new来声明实例:这个关键字创建对象并对其进行初使化。

    二、类:类中的数据和函数称为类的成员

    1.数据成员:是包含类的数据——字段、常量、事件的成员,数据成员可以是静态数据,类成员总是实例成员,除非用static进行显示声明。一旦实例化PhoncCustomer对象,就可以使用语法Object.FieldName来访问这些字段,如下:

    1 PhoneCustomer Customer = new PhoneCustomer();
    2 Customer.FirstName="Simon";

    2.函数成员:提供了操作类中数据的某些功能,包括方法、属性、构造函数和终结器(finalizer)、运算符以及索引器。

      给方法传递参数可以通过引用或值传递给方法,在通过引用传递(ref,out)时,被调用的方法得到的就是这个变量,所以在方法内部对变量进行的任何改变在方法退出后仍旧有效,而如果变量通过值传递给方法,被调用的方法得到的是变量的一个相同副本,在方法退出后,对变量进行的任何修改会丢失。对于复杂的数据类型,按引用传递效率更高,因为按值传递时必须复制大量的副本。在C#中,除非特殊说明,所有的参数都是通过值来传递。请看下面的例子:

     1 public static int Main() 
    2 {
    3 int i = 0;
    4 int[] ints = {0,1,2,4,8};
    5 Console.WriteLine("i = " + i);
    6 Console.WriteLine("ints[0] = " + ints[0]);
    7 Console.WriteLine("Calling SomeFunction...");
    8 someFunction(ints, i);
    9 Console.WriteLine("i = " + i);
    10 Console.WriteLine("ints[0] = " + ints[0]);
    11 return 0;
    12 }
    13
    14 static void someFunction(int[] ints,int i)
    15 {
    16 ints[0] = 100;
    17 i=100;
    18 }

    输出结果为:

    i=0
    ints[0]=0
    Calling SomeFunction...
    i=0
    ints[0]=100

    注意,i的值保持不变,而在ints中改变的值在原始数组中也改变了!string虽然是引用类型,但是在传递时,对字符串的任何改变不会影响原始字符串。

    如果想让方法体内的改变影响到方法外,用ref或out关键字修饰即可。

    命名参数:一般参数需要按定义的顺序传送给方法,而命名参数允许按任意顺序传递。如:

     string FullName(string firstName,string lastName)
    {
    return firstName+""+lastName;
    }
    下面的调用会返回相同的全名:
    FullName("Join","Doe");
    FullName(lastName:"Doe",firstName:"Join");

    可选参数:必须为可选参数提供默认值,可选参数必须是方法定义的最后一个参数。

    方法的重载:C#支持方法重载——方法的多个版本有不同的签名(方法名相同,但参数个数或类型不同)。但是2个方法不能仅在返回类型上有区别,也不能根据参数声明为ref和out来区分。

    属性:它是一个方法或一对方法,在客户端代码看来,它是一个字段,如Windows的Height属性:mainForm.Height=400;属性有只读只写属性、自动属性(不能严重有效性,必须为可读写属性)。

    3.构造函数:一个与类同名没有返回值的方法。如果不显示声明,系统会自动创建一个默认构造函数。一旦显示声明构造函数,编译器就不会提供默认的构造函数。

      C#的一个特性是可以给类编写一个无参的静态构造函数,这个函数只执行一次,而实例构造函数,只要类创建对象就会执行。编写静态构造函数的一个原因是:类的某些静态字段和属性,需要在第一次使用类之前,从外部源中初始化这些字段和属性。

      构造函数初始化器:实现从构造函数调用其他构造函数,构造函数初始化器在构造函数的函数体之前执行,用this关键字修饰,如下:

     1 class Car
    2 {
    3 private string description;
    4 private int nWheels;
    5 public Car(string description,int nWheels)
    6 {
    7 this.description=description;
    8 this.nWheels=nWheels;
    9 }
    10
    11 public Car(string description):this(description,4)
    12 {
    13
    14 }
    15 }

    在执行Car myCar=new Car("proton persona")时,会先执行带2个参数的构造函数,C#构造函数初始化器可以包含对同一个类的另一个构造函数的调用,也可以包含对直接基类的构造函数的调用,使用base关键字代替this。初始化器不能有多个调用。

      只读字段:类似常量,用readonly关键字修饰,比常量(const修饰)灵活的多,只读字段在运行前其值是未知的,只能在构造函数中给只读字段赋值,类的不同实例只读字段的值可以不同(实例只读字段),也可以是静态的。如果有一个用于编辑文档的MDI程序,因为需要注册,所以要限制可以同时打开的文档数,显然不能在源代码中对最大数目进行硬编码,而是需要一个字段表示,这时就要使用只读字段。注意如果在构造函数中没有给只读字段赋值,它将是特定数据类型的默认值。

    三、匿名类型:var关键字与new关键字一起使用时,可以创建匿名类型,匿名类型是一个继承自object且没有名称的类,该类的定义从初始化器中推断,类似隐式类型化的变量,如:

    var caption = new {FirstName="James",MiddleName="T",LastName="Kirk"};

    就会生成一个包含三个属性的对象。如果在创建一个类似的对象doctor,就可以设置caption=doctor。如果doctor的值来自于caption,可以简化初始化器,如下:

    var doctor=new {caption.FirstName,caption.MiddleName,caption.LastName};

    这些新对象的类型名未知,编译器为类型‘伪造’了一个名称,只有编译器才能使用它,我们不能使用新对象的任何类型反射。

    四、结构:在需要一个小的数据结构时,类提供的功能多于我们需要的功能,由于性能原因,这时最后使用结构。

      结构是值类型,他们存储在栈中或存储为内联(inline),结构不支持继承,编译器总是为结构提供一个无参的默认构造函数,它是不允许替换的,使用结构可以指定字段如何在内存中的布局。

      为结构创建实例时不需要使用new关键字,直接声明即可。注意在结构类型作为参数传递的时候,对结构赋值,结构的所有内容都被复制,而不是复制其引用,这时需要使用ref修饰,以免性能损失。

    五、部分类:partial关键字允许把类、结构或接口放在多个文件中。在多个开发人员需要访问同一个类,或者某种类型的代码生成器生成了一个类的某部分,把类放在多个文件中是有意义的。在嵌套类型中,只要partial关键字在class关键字前面,就可以嵌套部分类,在把部分类编译到类型中时,属性、接口、泛型的参数属性和成员都会合并。

    六、静态类:如果类只包含静态方法和属性,该类就是静态的,不能创建静态类的实例,静态类中的静态方法调用时只能通过类名调用。

    七、Object类:所有.net类的基类,如果定义一个类的时候没有指定基类,编译器就会自动假设这个类派生自Object。所有类都可以调用Object定义的许多公有和受保护的成员方法。如:ToString(),GetHashTable(),Equals()和ReferenceEquals(),Finalize(),GetType(),MemberwiseClone()等方法。

    八、扩展方法:在没有源代码时扩展类的功能时使用扩展方法。扩展方法是静态方法,它是类的一部分,但实际上没有放到该类的源代码中。如为Money类增加一个方法AddToAmount(decimal amountToAdd):

    1 public static class MoneyExtension
    2 {
    3 public static void AddToAmount(this Money money,decimal amountToAdd)
    4 {
    5 money.Amount+=amountToAdd;
    6 }
    7 }

      对于扩展方法,第一个参数是要扩展的类型,放在this关键字后面,告诉编译器这个方法是Money类的一部分。在扩展方法中可以访问所扩展类的所有公有方法和属性。在主程序中,
    AddToAmount方法看起来像是另一个方法,没有显示第一个参数,也不能对其做任何处理,调用时与其他方法相同:cash.AddToAmount(10m);,虽然扩展方法是静态的,但在调用时也要用标准的实例方法语法。如果类中存在与扩展方法同名的方法,此时调用时不会调用扩展方法。

  • 相关阅读:
    字号、pt、px、em换算对照表
    回车自动提交 禁止回车自动提交
    working copy locked (svn)
    xUnit asp.net单元测试工具基本使用
    防御网站攻击 1
    Access restriction: The type HttpServlet is not accessible due to restriction on required library xxxx\servletapi.jar
    【转载】将sqlserver表中的数据导出sql语句或生成insert into语句
    动态切换数据源(spring+hibernate)
    MSSQL2005移植到MYSQL 5.0
    C++ Primer 4 CPP Note 2.1.1 整型和浮点型
  • 原文地址:https://www.cnblogs.com/PaulMa/p/2248144.html
Copyright © 2011-2022 走看看