C#面向对象

C#面向对象(一)

 

一:面向对象的基本知识

C#程序分为面向过程和面向对象

什么是对象:一切皆为对象:Object,生活中常说的“东西”就是程序里面所指的对象;生活中遇到的东西我们都在下意识的归类;归类意味着抽象模型;

类:class,对某类众多对象的共同特点抽象出来的模型。

他们的关系:类是好多对象的抽象,对象是类的实例化。

创建一个名为Dog的类:

复制代码
复制代码
 class Dog  //一般首字母大写
    {
       int age;
       public void setage(int a) //用方法赋值
        {
           age = a;
        }
       public void buck()
        {
            Console.WriteLine("这是一个方法");
        }
    }
复制代码
复制代码

类中一般包括两类东西,变量(名词,也交成员变量这里age是成员变量)和函数(动词,也叫成员函数或成员方法 buck()就是方法)。

对象实例化:

Dog d1 = new Dog();

这样就实例化出了一个Dog的对象a。

例子:两个套在一起的圆,求内圆的周长和内圆与外圆之间的面积,用面向对象的思想做

复制代码
class circle
    {
        float r;
        public circle(float a)
        {
            r = a;
        }
        public double zhouchang()
        {
            return 2 * 3.14 * r;
        }
        public double mianji()
        {
            return 3.14 * r * r;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            circle m = new circle(10);
            circle n = new circle(20);
            double bc = m.zhouchang();
            double mj = (n.mianji()-m.mianji());
            Console.WriteLine("内圆的周长为:"+bc);
            Console.WriteLine("花砖的面积为:"+mj);
        }
    }
复制代码

首先做了一个名为circle的类,它可以生产任何一个半径不同的圆,在构造函数中,为半径赋值。

 

二:面向对象的三大特性

三大特性是指:封装,继承,多态。

类中的方法一般分为:构造方法(函数);属性方法(函数):成员变量赋值取值;行为方法(函数):变量运算。

(一): 封装

1.封装含义:

(1)不同类的变量只属于各自的类。

(2)不同对象的成员变量只属于各自的对象,彼此不受影响。

      (3)对象中的变量需要通过方法(函数)实现操作,比较安全。

封装为了安全,尽量不用public来声明变量,避免在main函数中可以直接访问赋值而降低了安全性,在类中建立public的方法来赋值,main中调用此方法传值。

2. 成员变量及访问修饰

private 私有成员 ,protected 受保护的成员,public 公有成员

3. 构造函数

它是一个特殊的成员函数,一般在构造函数里进行初始化。如果不写构造函数在new的时候会自动生成一个默认空的构造函数。

写法特殊:没有返回值,函数名只能与类名一样;public 类名(){};

执行特殊:类在实例化(new出来的时候)自动执行,构造函数是最早执行的成员函数,构造函数是用来生成对象的函数。

它的主要作用:对象实例化生成的时候,做一些初始化的工作。

下面例子就是做了一个Ren()的构造函数,为其变量赋上初始化的值:

复制代码
 class Ren
    {
        string _Name;
        int _Age;

        public Ren()
        {
            _Name = "龙神";
            _Age = 25;
        }
    }
复制代码

4. 重载(函数或方法)

函数名相同,参数不同(参数个数或类型不同)的多个函数就形成了重载。

重载只与函数名和形参有关,与返回类型无关。

下面举个例子,构造函数的重载:

复制代码
    class Ren()
        {
            string _Name;
            int _Age;

            public Ren()
            {
                _Name = "ZSMJ";
                _Age = 25;
            }
            public Ren(string name)
            {
                _Name = name;
            }
            public Ren(string name,int age)
            {
                _Name = name;
                _Age = age;
            }
        }
复制代码

这样Ren类中就做了三个构造函数,第一个是无参的,第二个是一个string类型的参数,第三个是两个参数,类型分别为string和int。他们符合重载的条件,所以会形成重载在Main函数中new的时候根据参数的不同会自动选择执行其中一个构造函数。

如果在Main函数中:

Ren a = new Ren("王大锤");

则执行的构造函数是第二个。

5.  属性

它的声明:public 类型 属性名;也可以选择成员变量右键重构,封装字段来生成属性方法,例如:

    string _Name;
    public string Name
   {
      get { return _Name; }
      set { _Name = value; }
   }

这样Name就是该类的一个属性,在Main函数中可以用此属性为其成员变量赋值取值:

     Ren a = new Ren("王大锤");
     a.Name = "王尼玛";

注意:(1)属性是用来为成员变量赋值和取值的,它有代替属性方法的作用,一般用属性。

(2)属性定义的时候,属性名后面没有小括号。

(3)属性都是public。

(4)属性中只能包含两个部分:get和set。代码也只能写在get和set的花括号里面。

(5)属性分为只读属性,只写属性和可读写属性,与get和set的有无有关系。

6. this关键字

概念:this引用,this在哪个对象里面执行代表该对象本身。

用法:this.成员变量(成员方法),this._Name; this.Eat();

例子:this 调用当前对象的其它构造函数。

复制代码
  public class Mydate
    {
        int _Year;
        int _Month;
        int _Day;
        int _Hours;

       public Mydate(int year,int month) 
       {
           _Year = year;
           _Month = month;
       }
       public Mydate(int year,int month,int day,int hours): this(year,month)
       {
           _Day = day;
           _Hours = hours;
       }
    }
复制代码

这里第一个构造函数中有两个参数year和month,在做第二个构造函数的时候为了方便用“:this”来调用了本类中第一个构造函数,这样第二个构造函数只需写后day和hours的执行语句就可以了。

在new的时候传入不同的参数,会实例出不同的对象,这时候的this就代表不同的对象。

7. is关键字(运算符)

用法:对象 is 类名;is左边是对象,右边是类型;

 Console.WriteLine(a is Ren);

如果a是一个Ren类的对象,则返回值为true,否则返回值为false。

8. partial关键字

如果一个类特别大,不宜挡在一个文件中实现或者一个类中有一部分代码不宜与其他混淆或者需要多人合作编写一个类,这就需要将一个类拆开来写。

用partial关键字可以实现,也可以用来补充完善类,扩展性比较强。
 
如在程序集里的一个文件里写了 partial class Ren{里面是成员} ; 在另一个文件里也可以对Ren类进行补充,需要这样写 partial class Ren{里面是成员} 。
 
9. 静态成员

非静态变量称为实例变量,非静态方法称为实例方法,实例成员的数据存在每个对象中,用对象名来调用。

静态成员包括:静态变量,静态属性,静态方法。

定义一个成员为静态的:在变量或方法前加static,如:static int a;

静态变量是属于类的,每个对象都有的并且相同的东西只保存一份,不和实例变量那样在每个对象里都面保存一份。

可以说它不属于任何对象,也可以说它又属于任何一个对象,给每个对象用,节省空间。

例如:每包粉笔的颜色是静态成员,每支粉笔的剩余长度是实例成员。

静态变量或方法不需要new出来。

在C#中,定义了一个粉笔的类:

复制代码
   class Fenbi
   {
       static string _Color;

    public static string Color
    {
      get { return Fenbi._Color; }
      set { Fenbi._Color = value; }
    }
       int _Lenght;

    public int Lenght
    {
      get { return _Lenght; }
      set { _Lenght = value; }
    }  
   }
复制代码

(1)在当前类的花括号外,静态成员只能用类名来调用,不能用对象名来调用,而实例成员只能用对象名来调用,不能用类名来调用。

      Fenbi.Color ="Yellow";
      Fenbi b = new Fenbi();
      b.Lenght = 10;

(2)在当前类的花括号内,静态方法只能直接调用静态成员,不能调用非静态成员,实例方法可以调用非静态和静态的成员。

     public static void xiezi()
       {
           Console.WriteLine("写出"+_Color+"的文字了");
       }
     public void change()
        {
            Console.WriteLine(_Color+"的粉笔长度变为:"+_Lenght);
         }

10.  拷贝

浅拷贝:传递引用,不赋值对象。
深拷贝:创建一个新的对象。

 

(二): 继承

1. 语法:public 子类名:父类名;如:class Dog:Pet。

2. 特点:单继承,一个父类可以派生多个子类,但每个子类只能有一个父类

             如果一个类,没有明确指定父类是谁,默认是object。除了object类之外,所有类都有一个父类。

             子类可以从父类继承下父类的成员变量和成员方法。

3. 访问修饰符合访问权限:

private 的成员不被继承,只能在该类中访问。

protected成员可以被继承,能在该类和派生类中访问到,在外界访问不到。父类中的变量一般用protected。

public成员可以被继承,能在所有地方访问到。

4. base 关键字:子类中可以用(base.父类中的成员 )来调用父类中的成员,base()调用父类构造,base.xxx()调用父类成员方法。

调用的参数值会被覆盖,方法也会被覆盖。

5. 继承关系中实例化子类的流程:

先执行父类的构造函数,再执行子类的构造函数。

6. 继承关系的实例化:

如果父类的构造函数没有空参构造函数,全是带参数的,则子类必须要写构造函数,构造函数的形参中必须包含父类构造函数所需要的参数,还要用base()把父类构造函数所需要的参数传递父类。

      例如:父类中只有这个构造函数

      public Ren(string name, int age)
      {
          _Name = name;
          _Age = age;
      }

那么子类中的构造函数应该这样写:

      public Chinese(string name, int age, string yuyan):base(name,age)
        {
            _Yuyan = yuyan;
        }

7. sealed 关键字:

如果用来修饰class,称为密封类,此类无法被继承;如果用来修饰方法,该方法无法被重写。

如: sealed class Ren{};

 

(三): 多态

1. 概念:父类引用指向不同子类实例的时候,父类引用所调用的函数都是子类的函数,由于子类对象不同,父类引用调用的成员表现出来的不同状态就是一种多态。

2. 实现的方式:多态需要通过继承来实现

3. 分类:分为编译多态(重载overload)和运行多态(重写override)。父类方法被重写了之后也可以在子类中用base.方法 调用。

4. virtual关键字:虚方法,允不允许重写,要重写父类方法必须是虚方法:public virtual void Eat()。

5. 运行多态实现的条件:

(1)子类对父类方法的重写(override),父类和子类中都有相同的方法。

(2)父类引用指向子类实例。

例如,有一个Ren类是父类,一个Chinese类和American是子类,Ren r = new Chinese();父类的引用 r 指向子类实例。

复制代码
    class Ren
    {
        protected string _Name;
        protected string _Country;
        public virtual void Eat()
        {
            Console.WriteLine("正在吃饭...");
        }
    }
    class American : Ren
    {
        public override void Eat()
        {
            Console.WriteLine("正在用叉子和刀子吃饭....");
        }
    }
    class Chinese : Ren
    {
        public override void Eat()
        {
            Console.WriteLine("正在用筷子吃饭...");
        }
    }
复制代码

父类Ren中有个Eat方法为虚方法,在子类Chinese和American中进行了重写,在Main函数中:

复制代码
    Random rand = new Random();
    int n = rand.Next(100);
    Ren a;
    if (n % 2 == 0)
    {
        a = new American();
    }
    else
    {
        a = new Chinese();
    }
    a.Eat();
复制代码

随机让父类引用 a 指向 不同的子类实例,这样用父类引用 a 调用方法Eat()的时候表现出不同对象的操作。

6. 里氏代换原则和抽象依赖原则

里氏代换原则,如果某个方法接收的是父类引用,可以向里面传父类或其子类的元素,子类对象替代父类对象。

抽象依赖原则,用父类的引用来指向子类的实例。

例子:怪兽吃人,传入Ren的引用 r ,则r.Cry()表现出来不同的结果。

复制代码
class Monster
    {
        public void EatFood(Ren r)   //r = a;
        {
            r.Cry();
            Console.WriteLine("人类真好吃,吃饱了!");
        }
    }
    class Ren   
    {
        public virtual void Cry()
        {
            Console.WriteLine(".......");
        }
    }
    class American:Ren
    {
       
        public override void Cry()
        {
              Console.WriteLine("MyGod,God bless me!");
        }
    }
    class Chinese:Ren
    {
        public override void Cry()
        {
            Console.WriteLine("天哪,老天爷保佑我!");
        }
    }
复制代码

在Main函数中 ,先实例出一个怪兽,随机生成一个Ren的对象,将此对象的引用传入怪兽类里,通过这个引用来表现出不同的状态。          

复制代码
            Monster m = new Monster();
Random rand = new Random(); int n = rand.Next(100); if (n % 2 == 0) { American a = new American(); //或者是这样写 Ren a = new American(); m.EatFood(a); } else { Chinese c = new Chinese(); //或者是这样写 Ren c = new Chinese(); m.EatFood(c); }
复制代码
 
 
 

一:抽象方法

1. 在面向对象编程语言中抽象方法指一些只有方法声明,而没有具体方法体的方法。抽象方法一般存在于抽象类或接口中。

    在一些父类中,某些行为不是非常明确,因此无法用代码来具体实现,但是类还必须具备此方法,因此,把这样的方法定义为抽象方法。

2. 声明方法:public abstract Eat(); 方法声明只是以一个分号结束,并且在签名后没有大括号,没有函数体,因为太抽象不清楚,具体的实现由各个子类中重写函数实现。

3. 它的特点:

(1) 抽象方法是隐式的 virtual 方法。

(2) 只允许在抽象类中使用抽象方法声明。

(3) 因为抽象方法只声明不提供实实现,所以没有方法体。抽象方法只在派生类中真正实现,这表明抽象方法只存放函数原型(方法的返回类型,使用的名称及参数),而不涉及主体代码。

(4) 加abstract关键词。

(5)抽象方法的目的在于指定派生类必须实现与这一方法关联的行为。

二:抽象类

1. 抽象类:无法被实例化的类。关键词是abstract,凡是带有abstract关键词的类都无法被new出来。抽象类是不完整的,它只能用作基类。在面向对象方法中,抽象类主要用来进行类型隐藏和充当全局变量的角色。

2. 声明:抽象类声明:public abstract class Ren{};

3. 注意:

(1)凡是带有抽象方法的类肯定是抽象类;抽象类却不一定包含抽象方法。

(2)构造方法,静态成员方法不能声明为抽象方法。

(3)一个非抽象类必须实现从父类继承来的所有抽象方法,如果有一个抽象方法没有实现,则此类必须要加abstract关键字。如果父类被声明为抽象类,并存在未实现的抽象方法,那么子类就必须实现父类中所有的abstract成员,除非该类也是抽象的。

4. 特征:

(1)抽象类不能实例化。

(2)一个抽象类可以同时包含抽象方法和非抽象方法。

(3)不能用sealed修饰符修饰抽象类,因为这两个修饰符的含义是相反的,采用sealed修饰符的类无法继承,而abstract修饰符要求对类进行继承。

(4)从抽象类派生的非抽象类必须包括继承的所有抽象方法和抽象访问器的实际实现。

例子:Ren类中有一个抽象方法Eat(),在其派生类Chinese和American中必须重写这个方法

复制代码
abstract class Ren
    {
        protected string name;
        public abstract void Eat();
    }

    class Chinese:Ren
    {
        public override void Eat()
        {
            Console.WriteLine("用筷子吃饭");
        }
    }
    class American : Ren
    {
        public override void Eat()
        {
            Console.WriteLine("用刀叉吃饭");
        }
    }
复制代码

三:接口

1. 关键字:interface,用interface 关键词来定义。

2. 概念:极度抽象的类,无成员变量,无实例属性和实例方法,只有抽象方法或抽象属性,生活中的例子:标准,规则。

 

3. 写法:接口不用class,用interface,名字一般以I作为首字母;不用写abstract,里面所有都是,不用写public,必须是public。

    interface IUSB         //接口
    {
        void start();
        void stop();
    }

4. 特点:

(1)接口中的方法都是抽象的,因此无需加abstract修饰符。

(2)接口中的方法都是公用的,因此无需加public修饰符。

(3)接口就是一个规则标准。

(4)接口可以继承父接口。

(5)一个类可以实现(继承)多个接口。一个类只能有一个父类,但可以实现多个接口。

例子:简单的IUSB接口,里面有两个抽象方法start()和stop(),派生类实现接口必须实现接口中的所有方法。

复制代码
 interface IUSB         //接口
    {
        void start();
        void stop();
    }
    class UDisk : IUSB  //实现接口必须实现里面的所有方法
    {
        public void start()
        {
            Console.WriteLine("U盘启动了");
        }
        public void stop()
        {
            Console.WriteLine("U盘停止了");
        }
    }
    class Cammer : IUSB
    {

        public void start()
        {
            Console.WriteLine("摄像头启动了");
        }

        public void stop()
        {
            Console.WriteLine("摄像头关闭了");
        }
    }

    class computer
    {
        public void CheckUSB(IUSB usb)
        {
            usb.start();
        }
        public void CloseUSB(IUSB usb)
        {
            usb.stop();
        }
    }
复制代码

用的时候:

复制代码
       computer c = new computer();
       UDisk u = new UDisk();
       Cammer m = new Cammer();

       c.CheckUSB(u); //插入U盘
       c.CheckUSB(m); //插入摄像头

       c.CloseUSB(u); //拔出U盘
       c.CloseUSB(m); //插入摄像头
复制代码

四:命名空间

1. 概念:namespace 相当于一个包,它是对类进行分类管理的工具,给计算机看的,相同命名空间下的类可以分在不同的文件中存储。

2. 一般写法:namespace 公司名.项目名.模块名

3. 在别的命名空间上面要使用其它命名空间内的类,可以在它的上面写 using 要使用的命名空间;

五:项目名

一般默认:ConsoleApplication1,用来给人类标识出这个项目是做什么的,给人看的。

六:程序集

在项目名上打右键:属性,即可修改程序集名称

编译出来的exe或dll文件就是程序集。exe是控制台应用程序,dll是库类。

程序集的名字就是编译出来的exe或dll名字。

如果一个项目分三个人来写,A写一个类库,B写一个类库,C写Main函数,把他们整合在一起的流程是:

1. A写完了编译出一个名字如 cc.dll 的文件;

2. B写的时候要用到A命名空间中的某个类,B先将A的cc.dll文件拷贝到自己的目录下,B需要在项目中的引用上打右键:添加引用,通过浏览找到该引用添加进来,然后using A 的命名空间;

3. B写完了编译出一个名字如 dd.dll 的文件;

4. C要写Main函数,在Main函数中操作AB中的类,他需要将AB的dll文件拷贝过来,然后再项目中的引用上打右键,添加引用,通过浏览找到该引用添加进来,然后using A 的命名空间; using B的命名空间,这样就可以在Main函数中用了。

5. 如果A的命名空间中有个类Ren和B的命名空间中有个类Ren重名了,这C在用的时候要用哪一个命名空间下的Ren类就要用该命名空间点出来再用。

 
posted on 2017-11-28 22:46  奋斗的笨小孩  阅读(8565)  评论(0编辑  收藏  举报