zoukankan      html  css  js  c++  java
  • 五、C# 类

    面向对象编程
    类是面向对象编程的3个主要特征---封装、继承和多态性---的基础。
     
    封装允许隐藏细节。
     
    继承
    继承关系至少涉及两个类,其中一个类(基类)是另一个类的更泛化的版本。
     
    为了从一个类型派生或者说从它继承,需要对那个基类型进行特化,这意味着要对基类型进行自定义,针对特定的目的调整它。
    继承最关键的一点在于,所有派生类型都继承了基类型的成员。在派生类型中,
    基成员的实现通常可以改动,但是不管是否修改基成员,派生类型中除了:有派生类型包含的那些
    成员外,还可以包含基类型的成员。
     
     
    多态性意味着一个方法或类型可以具有多种形式的实现。
     
    对象和类
     
    类相当于一个模块,对象是这个类的一个实例,相当于这个用这个模块创造一个产品。
    使用类创建一个新对象的过程称为实例化。
     
    实例化一个对象,需要使用new运算符指示“运行时”为一个对象分配内存,实例化对象,
    并返回对实例的一个引用(整型数组)。
     
    在堆中申请的内存(如new 申请分配),由垃圾回收器自动回收。(运行时,会在最后一次
    访问对象之后,并在应用程序结束之前的某个时候,自动地回收内在。)
     
    实例字段(成员变量)
    可以在声明时设置字段的初始值。
     
    实例方法(成员方法)
    可以访问对象上的字段。
     
    this关键字
    在类的实例成员内部可以获取当前对象的一个引用。
    this在概念上是传给每个实例方法的一个隐式参数,它返回对象本身的一个实例。
     
     
    访问修饰符
     
    在面向对象编程中,封闭不仅仅是将数据和行为组合到一起,它同时还意味着隐藏一个类中的数据,
    使一个类的内部工作机制是最小程度地对类的外部公共,这减少了调用者对数据进行不恰当修改的几率。
     
    访问修饰符是用途是提供封装。
    标识了所修饰成员的封装级别。
    public、private、protected、internal和protected internal 总共5个。
    注:如果不为类成员添加访问修饰符,那么默认使用的是private。
     
    属性
    为了实现某些字段从外部只读等的效果。一般采取的办法是将字段标记为私有,然后提供取值
    和赋值方法来访问和修改数据,只读时,只提供取值方法,不提供赋值方法。
    C#提供了显式的语法支持:属性。
     
     1 class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             string firstName = "aaaaa", middleName = "bbbbbbbbb", lastName = "cccccccc";
     6             string fullName;
     7             Employee e = new Employee();
     8             e.FirstName = firstName;
     9             e.LastName = lastName;
    10             Console.WriteLine(e.FirstName + "." + e.LastName);
    11         }
    12     }
    13    public class Employee
    14     {
    15         private string _FirstName;
    16         public string FirstName
    17         {
    18             set
    19             {
    20                 _FirstName = value;
    21             }
    22             get
    23             {
    24                 return _FirstName;
    25             }
    26         }
    27         private string _LastName;
    28         public string LastName
    29         {
    30             set
    31             {
    32                 _LastName = value;
    33             }
    34             get
    35             {
    36                 return _LastName;
    37             }
    38         }
    39  
    40     }
     
    属性的关键点在于,它提供了从编程角度看类似于字段的API,但是事实上,并不存在这样的字段。
    在大括号中,要添加具体的属性实现代码。两个可选的部分构成了一个属性的实现。
    set get
    自动实现的属性 C#3.0
    声明一个属性时,不实际地实现任何取值或同仁方法,也不声明任何支持字段
    1   public class Employee
    2     {
    3         
    4         public string Title { set; get; }
    5         public Employee Manager { set; get; }
    6  
    7     }
    使用属性的好处在于,如果需要添加一个额外代码:比如在set方法中添加验证,数据的合法性。
    虽然属性的声明发生了变化,但是调用属性的代码不进行任何更改。
     
    建议使用属性,来调用字段,不建议直接调用字段。
     
    实现只读和只写属性:
    通过移除某个属性的取值方法或赋值方法部分,可以改变属性的可访问性。
     
    为取值方法和赋值方法指定访问修饰符
     
      
     1 public class Employee
     2     {
     3         private string _FirstName;
     4         public string FirstName
     5         {
     6             private set
     7             {
     8                 _FirstName = value;
     9             }
    10             get
    11             {
    12                 return _FirstName;
    13             }
    14         }
    15         private string _LastName;
    16         public string LastName
    17         {
    18             private set
    19             {
    20                 _LastName = value;
    21             }
    22             get
    23             {
    24                 return _LastName;
    25             }
    26         }
    27         public string Title { private set; get; }
    28         public Employee Manager { private set; get; }
    29  
    30     }
    注:这个访问修饰限制性必须比应用于整个属性的访问修饰符更为严格。
     
    属性的赋值和取值,可以是任何操作,get 必须返回一个数据类型一致的数据。
     
    属性和方法调用不允许作为ref或out参数值使用
     
    原因:ref和out需要将内存地址传给目标方法,但是,由于属性可能是没有支持字段的虚字段,
    也有可能是只读/只写的,因此不可能传递其基础存储的地址,不过可以通过中间变量来实现。
     
     如:
    1          public string Title 
    2         {
    3             private set;
    4             get
    5             {
    6                 return _FirstName + "." + _LastName;
    7             }
    8         }    
     
    构造器(构造函数)
    为了定义构造器,要创建一个返回类型的方法,而且方法名必须完全和类名相同。
    构造器是用来创建对象实例的方法。
     
    new运算符返回的是被实例佛手对象的类型。
    注:如果一个字段在声明时赋了初始值,且在构造函数中也赋予了初始值,那么最终生效的是构造器
    内部的赋值,它会覆盖声明时的任何赋值。
    所以应该避免在两个地方同时赋值。
     
    C#编译器会自动添加一个默认构造器(无参数),且如果自定义了构造函数,不再自动添加默认构造函数。
     
    C#3.0对象初始化器
    在调用的时候使用
     
     1 class Program
     2     {
     3         static void Main(string[] args)
     4         {
     5             string firstName = "aaaaa", middleName = "bbbbbbbbb", lastName = "cccccccc";
     6             string fullName;
     7             Employee e = new Employee(firstName, lastName) { Title = "MyTitle", Content="Content" };
     8  
     9             Console.WriteLine(e.Title + ":" + e.Content + "" + ":" + e.FirstName + "." + e.LastName);
    10             Console.ReadLine();
    11  
    12  
    13         }
    14  
    15  
    16     }
    17     public class Employee
    18     {
    19         public Employee(string firstName, string lastName)
    20         {
    21  
    22             FirstName = firstName;
    23             LastName = lastName;
    24         }
    25         private string _FirstName;
    26         public string FirstName
    27         {
    28             private set
    29             {
    30                 _FirstName = value;
    31             }
    32             get
    33             {
    34                 return _FirstName;
    35             }
    36         }
    37         private string _LastName;
    38         public string LastName
    39         {
    40             private set
    41             {
    42                 _LastName = value;
    43             }
    44             get
    45             {
    46                 return _LastName;
    47             }
    48         }
    49         public string Title
    50         {
    51             set;
    52             get;
    53  
    54         }
    55         public string Content
    56         {
    57             set;
    58             get;
    59  
    60         }
    61         public Employee Manager { set; get; }
    62  
    63     }
     
    终结器
    终结器是在一个对象最后一次活动之后,并在程序终止之前执行,具体地说,垃圾回收器会在
    一次垃圾回收过程中识别出带有终结器的对象。之后,它并不是立即回收这些对象,而是将它们添加
    到一个终结列中。一个独立的线程遍历终结队列中的每一个对象,调用它们的终结器,然后把它们从队列
    中删除,使它们再次可供垃圾回收器调用。
     
    注:构造器可重载
     
    使用this调用另一个构造器
    这称为构造器链,它是用构造器初始化器来实现的。构造器初始化器会在执行当前构造器的实现之前
    判断要调用另外哪一具构造器,并进行调用。
      
     1         public Employee(string firstName, string lastName)
     2         {
     3  
     4             FirstName = firstName;
     5             LastName = lastName;
     6         }
     7         public Employee(string Title, string firstName, string lastName)
     8             : this(firstName, lastName)
     9         {
    10             this.Title = Title;
    11         }
     
    注:可写一个函数专门用来初始化,然后在构造器中调用。
     
    匿名类型
    在C#3.0中引入了对匿名类型的支持。它们是由器动态生成的数据,而不是通过显式的类定义来声明的。
     
    var 
     
    编译器遇到匿名类型的语法时,就会自动生成一个CIL类,该类具有与匿名类型声明中的命名值
    和数据类型对应的属性。
     
    静态成员 
     
    使用static修饰
     public static int Count = 0;
     
    与实例字段不同,如果不对静态字段进行初始化,静态字段将自动获得相应数据类型的默认值。
    另外,一个静态字段即使没有显式地赋值,也可以被访问。
    在类的外部访问静态字段时,需要使用类名。
     
    注:上下文就是作用域
     
    静态方法
    和静态字段一样,要直接在类名之后访问静态方法。
    由于静态方法不是通过一个特定的实例来引用,所以this关键字在静态方法中是无效。
    所以也不能直接在静态中访问类中声明的实例成员,所有实例成员必须通过一个对象引用才能访问。
     
     
    静态构造器
    除了静态字段和方法,C#还支持静态构造器。静态构造器用来对类进行初始化。静态构造器不是显式调用的
    ,运行时会在首次访问类时自动调用静态构造器。
    所谓“首次访问类”,可能是调用一个普通构造器,也可能是访问类的一个静态方法或者字段。
    在构造器中的赋值会覆盖声明时的初始化值。
     
     
    静态属性
     
    与实例属性一样,只是是属于类的。
    使用静态属性几乎肯定要比使用公共静态字段好,因为公共静态字段在任何地方都能调用,而静态
    属性则至少提供了一定程度的封装。
     
     
    静态类
    使用static修饰
    这个类不包含任何实例成员,所以创建一个实例化的类是没有意义的。
    静态类的另一个特殊在于,C#编译器会自动在CIL代码中把它标记为abstract和sealed。
    这会将类指定为不可扩展,换言之,不能从它派生出其他类。
     
     
     
    扩展方法(必须在静态类中定义)
    C#3.0
    引入了扩展方法(extension method)的概念。它能为一个不同的类模拟出一个实例方法。
    使静态方法的第一个参数成为要扩展的类型,并在类型名称前面附加this关键字。
          
     1   public static class DirectoryInfoExtension
     2           {
     3                 public static string Message(this Employee e, string message1, string message2)
     4                 {
     5                     return message1 + message2;
     6                 }
     7             }
     8            string firstName = "aaaaa", middleName = "bbbbbbbbb", lastName = "cccccccc";
     9             string fullName;
    10             Employee e = new Employee();
    11  
    12             Console.WriteLine(e.Title + ":" + e.Content + "" + ":" + e.FirstName + "." + e.LastName);
    13            
    14              Console.WriteLine(e.Message(firstName,lastName));
    15              Console.ReadLine();
    C#通过这一处小小的改进,就使我们能为任何类添加实例方法,即使是那些不在同一个程序集中的类。然后,根据最终生成的CIL代码,你会发现扩展方法和一个普通的静态方法的代码是完全一样的。
     
    扩展方法的要求如下:
    1、第一个参数是要扩展或者操作的类型,这称为"被扩展的类型"
    2、为了指定扩展方法,要在扩展的类型名称前面附加this修饰符
    3、要将方法作为一个扩展方法来访问,要用using指令导入扩展类型的命名空间,或者
    使扩展类和调用代码在同一个命名空间中。
    注:如果扩展方法的签名已经和扩展类型中的一个签名匹配,扩展方法永远不会得到调用,除非是作为
    一个普通的静态方法。
     
    应该尽量少用扩展方法,而使用继承来扩展。
     
    封装数据 
    两个特殊的字段修饰符,const、readonly
    1、const
    和const局部变量一样,const字段(常量字段)包含的是在编译时确定的一个值,它不可以在运行时改变。
     
    常量字段自动成员静态字段,因为不需要为每个对象实例都生成一个新的字段实例。
    但是,假如将一个常量字段显式地声明为static,会造成一个编译错误。
     
    public 常量应该是恒定值,否则,如果对它进行了修改,那么在使用它的程序集中,不一定能反映出
    这个修饰。需要重新编译。
    将来可能改变的值应该指定为readonly,不要指定为常量。
     
    2、readonly
    和const不同,readonly修饰符只能用于字段(不能用于局部变量),它指出字段值只能从构造器
    中更改,或者直接在声明时指定。
    每个实例的readonly字段都可以是不同的,除此之外,readonly字段即可以是实例字段,也可以是静态
    字段。另一个关键区别在于,可以在执行时为readonly字段赋值,而非只能在编译时赋值。
     
    嵌套类
     
    在类中除了定义方法和字段,还可以定义另一个类。这样的类称为嵌套类
    嵌套类中的this成员指的是嵌套类的一个实例,而不是包容类。
    嵌套类的另一个有趣的特点是它能够访问包容类的任何成员,其中包括私有成员。但反之则不然,包容类不能访问嵌套类的私有成员。
     
    分部类
    C#2.0新增的另一个语言特性是分部类(partial class)。
    使用partial修饰
    分部类是一个类的多个部分,这些部分可以合并成一个完整的类。
    使用上下文关键字partial来声明一个分部类。
     
    可以放在不同的文件当中。
    分部类不允许对编译好的类(其他程序集中的类)进行扩展。只能利用分部类在同一个程序集
    中将一个类的实现分解到多个文件中。
     
    分部方法
    C#3.0
    使用partial修饰
    分部方法只能存在于分部类中。另外和分部类相似,其主旨是为代码的生成提供方便。
    分部方法允许声明一个方法而不需要一个实现。
    然后,如果包含了可选的实现,这个实现就可以放到某个姊妹分部类定义中。
    也就是声明和定义分别放在分部类的不同地方。
     
    注:分部方法必须返回void,如果不是返回null,同时没有提供实现,那么调用一个未实现的方法。
    返回什么才合理呢。所以只允许方法返回void,out参数在分部方法中是不允许的。如果
    需要一个返回值,可以使用ref参数。
     
    总之,分部方法使生成的代码能调用并非一要实现的方法。
     
     
     
     
  • 相关阅读:
    FZU-Problem 2150 Fire Game
    LeetCode120——Triangle
    Coder-Strike 2014
    AP INVOICES IMPORT API(NOT request)
    NYOJ-277-车牌号
    软件測试方法
    C++中字符数组和字符串string
    【机器学习算法-python实现】PCA 主成分分析、降维
    主题讲座:移动互联网时代的创业机会
    ubuntu环境eclipse配置
  • 原文地址:https://www.cnblogs.com/tlxxm/p/4604522.html
Copyright © 2011-2022 走看看