C#基础笔记(2)

类和结构
类和结构实际上都是创建对象的模板,每个对象都包含数据,并提供了处理和访问数据的方法。
类是存储在堆(heap)上的引用类型,而结构是存储在堆栈(stack)上的值类型。较小的数据类型使用结构可提高性能。但在语法上,结构与类非常相似,主要的区别是使用关键字struct代替class来声明结构。在大多数情况下,类要比结构常用得多。
类:
  class PhoneCustomer
   {
    public const string DayOfSendingBill ="Monday";
   public int CustomerID;
    public string FirstName;
    public string LastName;
   }
结构:
  struct PhoneCustomerStruct
   {
     public const string DayOfSendingBill = "Monday";
     public int CustomerID;
     public string FirstName;
     public string LastName;
   }
对于类和结构,都使用关键字new来声明实例:这个关键字创建对象并对其进行初始化。
PhoneCustomer myCustomer = new PhoneCustomer();     //works for a class
PhoneCustomerStruct myCustomer2 = new PhoneCustomerStruct();   // works for a struct

一.类的封装:
1.数据成员: 字段、常量和事件。
   数据成员可以是静态数据或实例数据。不做特殊说明时,类成员总是实例成员,除非用static进行了显式的声明。
   静态成员属于类的,在所有的对象中都使用一个存储空间
   实例成员属于对象的,在每一个对象中都使用一个独立的存储空间
2.函数成员:函数成员提供了操作类中数据的某些功能,包括方法、属性、构造函数和终结器(finalizor)、运算符以及索引器。
  (1)方法
    a.方法的声明
     [modifiers] return_type MethodName([parameters])
     {
       // Method body
     }
     如果方法没有返回值,就把返回类型指定为void,因为不能省略返回类型。
    b.调用方法
     函数名(实参列表);
    c.给方法传递参数
    ref关键字:迫使值参数通过引用传送给方法。如果把一个参数传递给方法,且这个方法的输入参数前带有ref关键字,则该方法对变量所作的任何改变都会影响原来对象的值。
      在调用该方法时,还需要添加ref关键字:
     SomeFunction(ints, ref i);
    out关键字:使函数能从函数内向函数外输出多个值。当在方法的输入参数前面加上out关键字时,传递给该方法的变量可以不被初始值初始化。
      在调用该方法时,还需要添加out关键字:
     SomeFunction(out i);
    方法的重载:方法名相同,但参数个数与参数类型不同。不能够根据方法的返回值形成重载。
    
(2)属性
    用来给成员变量赋值与取值的功能。
    private string foreName;
    public string ForeName
    {
      get
      {
         return foreName;
      }
      set
      {
         if (value.Length > 20)
            // code here to take error recovery action
            // (eg. throw an exception)
         else
            foreName = value;
      }
    }
    只有get部分的属性称为只读属性,只有set部分的属性称为只写属性
   不能在get或set之间加访问修饰符
  (3).构造函数
    如果没有显式地提供任何构造函数,编译器会在后台创建一个默认的构造函数。
    构造函数没有返回类型,且函数名与类名同名。
    构造函数可以形成重载。
   
   静态构造函数:可以给类编写无参数的静态构造函数。这种构造函数只执行一次,而前面的构造函数是实例构造函数,只要创建类的对象,它都会执行。类有一些静态字段或属性,需要在第一次使用类之前,从外部源中初始化这些静态字段和属性。
     注意:
     静态构造函数没有定义访问修饰符,总是由.NET运行库调用它,所以像public 和 private这样的访问修饰符就没有意义了。
     静态构造函数不能带有任何参数
     一个类也只能有一个静态构造函数
     静态构造函数只能访问类的静态成员,不能访问实例成员。
      无参数的实例构造函数可以在类中与静态构造函数安全共存。尽管参数列表是相同的,但这并不矛盾。因为静态构造函数是在加载类时执行,而实例构造函数是在创建实例时执行。
      如果多个类都有静态构造函数,先执行哪个静态构造函数是不确定的。

    从其他构造函数中调用构造函数:
    class Car
     {
       private string description;
       private uint nWheels;
       public Car(string model, uint nWheels)
       {
          this.description = description;
          this.nWheels = nWheels;
       }
       public Car(string model) : this(model, 4)//构造函数初始化器
       {
       }
     }
     在本例中,在带有一个参数的构造函数执行之前,先执行带2个参数的构造函数
    
   (4)只读字段
    只读字段readonly类似于const常量,但与常量又有所不同。const常量在定义的时候就必须对它进行赋初值,在以后的使用过程中是不许进行改变的,而readonly形的变量在定义的时候可以不赋值,而在构造函数中对它进行初始化值,一旦赋值后就不可再更改其值,它类似于java中的final型的变量。
    readonly关键字比const灵活得多,允许把一个字段设置为常量,但在将字段设为常量之前可以执行一些运算,以指定它的初始值。
   在构造函数中给readonly字段赋值,但不能在其他地方赋值
    readonly字段还可以是一个实例字段,而不是静态字段,类的每个实例可以有不同的值
   如果要把readonly字段设置为静态,就必须使用static关键字进行显式声明;
    在构造函数中未给readonly 字段赋值,它的值就是其数据类型的默认值
   public class Document
    {
        public readonly DateTime CreationDate;
        public Document()
        {
           CreationDate = new DateTime(2002, 1, 1);
        }
    }

二、类的继承
C#不支持多重实现继承。而C#却允许类型派生于多个接口。因为System.Object是一个公共的基类,所以每个C#类(除了Object类之外)都有一个基类,还可以有任意多个基接口。

(1)类的继承语法:
  class MyDerivedClass : MyBaseClass
   {
     // functions and data members here
   }
(2)虚方法
   把一个基类函数声明为virtual,该函数就可以在任何派生类中重写了,否则默认基类中的函数是无法被重写的。也可以把属性声明为virtual。
  class MyBaseClass
   {
     public virtual string VirtualMethod()
     {
        return "This method is virtual and defined in MyBaseClass";
     }
     public virtual string ForeName
    {
      get { return foreName; }
      set { foreName = value; }
    }
   private string foreName;
   }
   在C#中,函数在默认情况下不是虚拟的,但(除了构造函数以外)可以显式地声明为虚拟。而在Java中,所有的函数都是虚拟的。
  在派生类的函数重写另一个函数时,要使用override关键字显式声明:
   class MyDerivedClass : MyBaseClass
   {
     public override string VirtualMethod()
     {
        return "This method is an override defined in MyDerivedClass";
     }
   }
(3)隐藏方法
   如果签名相同的方法在基类和派生类中都进行了声明,但该方法没有声明为virtual 和 override,派生类方法就会隐藏基类方法。
   成员字段和静态函数都不能被声明为virtual,因为这个概念只对类中的实例函数成员有意义。
   如果签名相同的方法在基类和派生类中都进行了声明,但该方法没有声明为virtual 和 override,派生类方法就会隐藏基类方法。结果是调用哪个类的方法取决于用于引用实例的变量类型,而不是实例本身的类型。
   系统会发出警告。在C#中,应使用new关键字声明我们要隐藏一个方法。
(4)调用函数的基础版本
   从派生类中调用方法的基础版本:base.<MethodName>()。
   class CustomerAccount
   {
     public virtual decimal CalculatePrice()
     {
        // implementation
       return 0.0M;
     }
   }  
   class GoldAccount : CustomerAccount
   {
     public override decimal CalculatePrice()
     {
        return base.CalculatePrice() * 0.9M;
     }
   }
   可以使用base.<MethodName>()语法调用基类中的任何方法,不必是在同一个方法的重载中调用它。
(5)抽象类和抽象函数
   抽象类不能实例化,而抽象函数没有执行代码,必须在非抽象的派生类中重写。
   如果类包含抽象函数,该类将也是抽象的,也必须声明为抽象的:
   abstract class Building
   {
     public abstract decimal CalculateHeatingCost();   // abstract method
   }
(6)密封类和密封方法
   把类和方法声明为sealed,对于类来说,这表示不能继承该类,对于方法来说;这表示不能重写该方法。
   类似于Java中的final。
   sealed class FinalClass
   {
     // etc
   }
   class DerivedClass : FinalClass       // 错误:密封类无法被继承
   {
     // etc
   }
   在方法上使用sealed关键字是没有意义的,如果定义一个新方法,但不想让别人重写它,不要把它声明为virtual就可以了。

(7)派生类的构造函数(车延禄)
   基类的构造函数总是最先调用。也就是说,派生类的构造函数可以在执行过程中调用基类方法、属性和其他成员,因为基类已经构造出来的,其字段也初始化了。
a.无参数的构造函数
   b.有参数的构造函数
    使用在子类构造函数之后使用base关键字调用父类构造函数,并传递构造实参

三、可见性修饰符


不能把类型定义为protected、internal和protected internal,这些修饰符只能应用于成员。
四、其他修饰符


五、接口
接口中只能包含方法、属性、索引器和事件的声明。
不能实例化接口,接口不能有构造函数或字段。
在接口定义中还不允许声明成员上的修饰符。接口成员总是公共的,不能声明为虚拟或静态。
1.定义和实现接口
    public interface IBankAccount
     {
       void PayIn(decimal amount);
       bool Withdraw(decimal amount);
       decimal Balance
       {
          get;
       }
     }
    接口名称传统上以字母I开头,以便知道这是一个接口。(车延禄)

2.接口的派生
   public interface ITransferBankAccount : IBankAccount
    {
       bool TransferTo(IBankAccount destination, decimal amount);
    }

六、结构
类在实例化过程中会在堆中创建对象,但性能会有一定的损失。有时仅需要一个小的数据结构,为了实现性能的优化我们可以使用结构替代类。
结构是值类型,不是引用类型。它们存储在堆栈中。
结构不支持继承。
结构中可以有方法。
结构有构造函数,编译器总是提供一个无参数的默认构造函数,这是不允许替换的。
结构可以实现接口。

结构定义
struct Dimensions
{
     public double Length;
     public double Width;
     Dimensions(double length, double width)
     { Length= length; Width= width; }
     public int Diagonal
     {
        {
           get
           {
              return Math.Sqrt(Length* Length + Width* Width);
           }
        }
      }
}
使用结构
Dimensions point = new Dimensions();
point.Length = 3;
point.Width = 6;
因为结构是值类型,所以new运算符与类和其他引用类型的工作方式不同。new运算符并不分配堆中的内存,而是调用相应的构造函数,根据传送给它的参数,初始化所有的字段。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值