C#学习笔记【五】——面向对象的编程

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/BreakingDawn0/article/details/102759824

5.1类的定义和声明

5.1.1构造函数

    我们构造对象的时候,对象的初始化过程是自动完成的,但是在初始化对象的过程中有的时候需要做一些额外的工作,例如需要初始化对象存储的数据,构造函数就是用于初始化数据的函数。

    声明基本的构造函数的语法就是声明一个和所在类同名的方法,但是该方法没有返回类型。

public class MyClass{

    public MyClass(){

       这个构造函数的函数体

    }

}

    当我们使用new关键字创建类的时候,就会调用构造方法。

    我们一般会使用构造方法进行初始化数据的一些操作。

    构造函数可以进行重载,跟普通函数重载是一样的规则。

    当我们不写,任何构造函数的时候,编译器会提供给我们一个默认的 无参的构造函数,但是如果我们定义了一个或者多个构造函数,编译器就不会再提供默认的构造函数。

5.1.2属性的定义

属性的定义结构:

    public int MyIntProp{

       get{

           // get code

       }

       set{

           //set code

       }

    }

    定义属性需要名字和类型。

    属性包含两个块 get块和set块。

    访问属性和访问字段一样,当取得属性的值的时候,就会调用属性中的get块,所以get块,类型需要一个返回值就是属性的类型;当我们去给属性设置值的时候,就会调用属性中的set块,我们可以在set块中通过value访问到我们设置的值。

5.1.3通过属性访问字段

    我们习惯上把字段设置为私有的,这样外界不能修改字段的值,然后我们可以通过定义属性来设置和取得字段中的值。

private int age;

public int Age{//习惯上属性大写 字段小写

    set{

       if(value<0)return;

       age = value;

    }

    get{

       return age;

    }

}

5.1.4设置属性的只读或只写

    属性可以值只提供一个set块或者get块

5.1.5属性访问修饰

5.1.6匿名类型

    我们创建变量(对象的时候),必须指定类型,其实我们也可以不去指定类型,这个就是匿名类型,我们可以使用var声明一个匿名类型。

    使用var声明的匿名类型,当初始化的时候,这个变量的类型就被确定下来,并且以后不可以修改。

var v = new Vehicle();
var v1 = "Lemon";
//var v1 = 1;//这里不允许,v1的类型已被确定为字符串

public class Vector3//设置为public这样在别的项目中才可以访问
    {
        //我们定义了一个构造函数,那么编译器不会为我们提供构造函数了
        public Vector3()
        {
            Console.WriteLine("Vector3的构造函数被调用了");
        }

        public Vector3(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
            length = Length();
        }

        //编程规范上习惯把所有的字段设置为private,只可以在类内部访问,不可以通过对象访问
        private float x, y, z, length;
        private int age;
        //private string name;

        //public String Name
        //{
        //    get { return name; }//可以通过对象获取属性的值
        //    private set { name = value; }//只可以在类内部设置属性的值
        //

        public string Name { get; set; }//编译器会自动给我们提供一个字段,来存储name(注意我们在定义字                                  //段的时候并没有定义name)

        public int Age
        {
            set
            {
                //通过set方法 在设置值之前做一些校验的工作
                if (value >= 0)
                {
                    age = value;
                }
            }
        }


        public float X// 也可以叫做get set方法
        {
            get { return x; }
            set { x = value; }//如果在get 或者 set前面加上 private,表示这个块只能在类内部调用

        }

        //为字段提供set方法,来设置字段的值

        public void SetX(float x)

        {
            //如果我们直接在方法内部访问同名的变量的时候,优先访问最近 (形参)
            //我们可以通过this.表示访问的是类的字段或者方法
            this.x = x;

        }


        public void SetY(float y)
        {
            this.y = y;
        }


        public void SetZ(float z)
        {
            this.z = z;
        }


        public float Length() {
            return (float)Math.Sqrt(x * x + y * y + z * z);
        }

        //定义属性
        public int MyIntProperty {
            set {
                Console.WriteLine("属性中set块被调用");
                Console.WriteLine("在set块中访问value的值是:"+value);
            }
            //如果没有get块,就不能通过属性取值了
            get {
                Console.WriteLine("属性中的get块被调用 ");
                return 100;
            }
        }
}

5.2继承

5.2.1继承的类型

实现继承:

   表示一个类型派生于一个基类型,它拥有该基类型的所有成员字段和函数。 在实现继承中,派生类型采用基类型的每个函数的实现代码,除非在派生类型的定义中指定重写某个函数的实现代码。 在需要给现有的类型添加功能,或许多相关的类型共享一组重要的公共功能时,这种类型的继承非常有用。

namespace c_sharp_001

{

    class Enemy

    {

        private int hp;

        private int speed;


        public int Hp

        {

            get { return hp; }

            set { hp = value; }

        }


        public int Speed

        {

            get { return speed; }

            set { speed = value; }

        }


        public void AI()

        {

            Console.WriteLine("这里是Enemy1的公有AI方法");

        }


        public void MoveI()

        {

            Console.WriteLine("这里是Enemy的公有Move方法");

        }

    }

}

namespace c_sharp_001

{

    class Boss:Enemy

    {

        public void Attack()

        {

            //hp = 100;

            Hp = 100;//父类里公有的数据和函数成员在子类里才可以访问

            AI();

            MoveI();

            Console.WriteLine("Boss正在攻击");

        }

    }

}

namespace c_sharp_001

{

    class Type1Enemy:Enemy

    {

    }

}

namespace c_sharp_001

{

    class Program

    {

        static void Main(string[] args)

        {

            Boss boss = new Boss();

            boss.AI();

            boss.Attack();


            Enemy enemy;

            enemy = new Boss();//父类声明的对象可以用子类构造                    

            Boss bossEnemy = (Boss)enemy;//enemy虽然使用了父类进行声明,但是用子类构造(实例化),本质上是一

                                         //个子类类型,我们可以强制类型转换成子类类型


            Enemy enemy1 = new Enemy();

            Boss bossEnemy1 = (Boss)enemy1;//强制类型转换错误,一个对象是什么类型,主要看它是通过什么构造的


            Console.ReadKey();

        }

    }

}

接口继承:

   表示一个类型只继承了函数的签名,没有继承任何实现代码。 在需要指定该类型具有某些可用的特性时,最好使用这种类型的继承。

多重继承(C#不支持):

    一些语言(C++)支持所谓的“多重继承”,即一个类派生自多个类。使用多重继承的优点是有争议的:一方面,毫无疑问,可以使用多重继承编写非常复杂但很紧凑的代码。另一方面,使用多重实现继承的代码常常很难理解和调试。如前所述,简化健壮代码的编写工作是开发 C#的重要设计目标。因此,C#不支持多重实现继承。而 C#允许类型派生自多个接口——多重接口继承。这说明,C#类可以派生自另一个类和任意多个接口。更准确地说, System.Object是一个公共的基类,所以每个C#(除了Object类之外)都有一个基类,还可以有任意多个基接口。

5.3虚方法

    把一个基类函数声明为virtual,就可以在任何派生类中重写该函数:

    class MyBaseClass{

       public virtual string VirtualMethod(){

           return "Method is called in base class";

       }

    }

    在派生类中重写另外一个函数时,要使用override关键字显示声明

    class MyDerivedClass:MyBaseClass{ 

       public override string VirtualMethod(){

           return "Method is called in derivedclass.";

       }

    }

    我们在子类里面重写虚函数之后,不管在哪里调用都是调用重写之后的方法

namespace c_sharp_001

{

    class Enemy

{

        public void AI()

        {

            Move();

            Console.WriteLine("这里是Enemy1的公有AI方法");

    }


        public virtual void Move()

        {

            Console.WriteLine("这里是Enemy的公有Move方法");

        }

    }

}

namespace c_sharp_001

{

    class Boss:Enemy

    {

        public override void Move()//重写:原来的方法不存在了

        {

            Console.WriteLine("这里是Boss的移动方法");

        }

        public void Attack()

        {

            Move();//子类里对Move进行重写,调用的子类里的Move函数

            Console.WriteLine("Boss正在攻击");

        }

    }

}

namespace c_sharp_001

{

    class Program

    {

        static void Main(string[] args)

        {

            Boss boss = new Boss();

            boss.Attack();

            boss.AI();//父类中AI方法调用的Move方法已在子类中重写

                      //我们在子类里面重写虚函数之后,不管在哪里调用都是调用重写之后的方法


            Enemy enemy = new Enemy();

            enemy.AI();//子类里重写的Move方法不会影响父类构造的对象

            Console.ReadKey();

        }

    }

}

5.4隐藏方法

    如果签名(就是方法的定义,返回值、参数、方法名)相同的方法在基类和派生类中都进行了声明,但是该方法没有分别声明为virtual和override,派生类就会隐藏基类方法。(要使用new关键字进行声明)

    基类

    class MyBaseClass{

        public int MyMethod(){

        }

    }

    派生类(在派生类中把基类同名的方法隐藏掉了)

    class MyDerivedClass :MyBaseClass{

        public new void MyMethod()        {

               }   

    }

namespace c_sharp_001

{

    class Enemy

    {

        public void Move()

        {

            Console.WriteLine("这里是Enemy的公有Move方法");

        }

    }

}

namespace c_sharp_001

{

    class Boss:Enemy

    {

        public new void Move()//当子类里有一个和父类签名相同的方法时,就会把父类中的方法隐藏

        {                     //隐藏:原来的方法还存在,只是父类中的方法被隐藏了

            Console.WriteLine("这里是Boss的移动方法");

        }

    }

}

namespace c_sharp_001

{

    class Program

    {

        static void Main(string[] args)

        {

            Boss boss = new Boss();

            boss.Move();//使用子类声明的对象,调用隐藏方法会调用子类的


            Enemy boss1 = new Boss();

            boss1.Move();//使用父类声明的对象,调用隐藏方法会调用父类的


            Console.ReadKey();

        }

    }

}

5.5.this和base关键字

    this可以访问当前类中定义的字段,属性和方法,有没有this都可以访问,有this可以让IDE-VS编译器给出提示。另外当方法的参数跟字段重名的时候,使用this可以表明访问的是类中的字段。

    base可以调用父类中的公有方法和字段,有没有base都可以访问,但是加上base.IED工具会给出提示,把所有可以调用的字段和方法罗列出来方便选择。

5.6抽象类

    C#允许把类和函数声明为 abstract。抽象类不能实例化,抽象类可以包含普通函数和抽象函数,抽象函数就是只有函数定义没有函数体。显然,抽象函数本身也是虚拟的Virtual(只有函数定义,没有函数体实现)。

    类是一个模板,那么抽象类就是一个不完整的模板,我们不能使用不完整的模板去构造对象。

    abstract class Building{

        public abstract decimal CalculateHeatingCost();

namespace c_sharp_001

{

    abstract class Bird//抽象类

    {

        private float speed;


        public void Eat()

        {


        }

        public abstract void Fly();

    }

}

namespace c_sharp_001

{

    class Crow : Bird//继承了一个抽象类,就必须去实现抽象方法

    {

        public override void Fly()

        {

            Console.WriteLine("乌鸦在飞行");

        }

    }

}

namespace c_sharp_001

{

    class Program

    {

        static void Main(string[] args)

        {

            Crow crow = new Crow();

            crow.Fly();


            Bird bird = new Crow();//可以通过抽象类去声明对象,但不能去构造对象

            bird.Fly();


            Console.ReadKey();

        }

    }

}

5.7密封类和密封方法

    C#允许把类和方法声明为sealed。对于类,这表示不能继承该类;对于方法(把重写的方法声明为sealed)表示不能再次重写该方法。防止重写某些类导致代码混乱或商业原因

    sealed FinalClass

    {

       // etc

    }

5.8派生类的构造函数

    在子类中调用父类的默认构造函数(无参)(会先调用父类的,然后是子类的)

    public class MyDerivedClass{

       public MyDerivedClass():base(){

           //do something

       }

    }

    在这里 :base()可以直接不写,因为默认会调用父类中的默认构造函数

    调用有参数的构造函数

    public class MyDerivedClass{

       public MyDerivedClass(string name):base(name){

           //do something

       }

    }

namespace c_sharp_001

{

    class BaseClass

    {

        private int x;

        public BaseClass()

        {

            Console.WriteLine("BaseClass无参构造函数");

        }

        public BaseClass(int x)

        {

            this.x = x;

            Console.WriteLine("x赋值完成");

        }

    }

}

namespace c_sharp_001

{

    class DerivedClass:BaseClass

    {

        private int y;

        public DerivedClass() : base()//调用父类中无参的构造函数

        {                             //当没有在子类中显示调用父类的构造函数,会默认调用父类的无参构造函数

            Console.WriteLine("DerivedClass无参构造函数");

        }

        public DerivedClass(int x,int y):base(x)

        {

            this.y = y;

            Console.WriteLine("y赋值完成");

        }

    }

}

5.9修饰符

    protected保护的,当没有继承的时候,它的作用和private是一样的,当有继承的时候,protected表示可以被子类访问的字段或者方法。

    static可以修饰字段或者方法,修饰字段的时候,表示这个字段是静态的数据,叫做静态字段或者静态属性;修饰方法的时候,叫做静态方法或者静态函数。使用static修饰的成员,只能通过类名访问,当我们构造对象的时候,对象中只包含了普通的字段不包含静态字段。

5.10接口

    定义一个接口在语法上跟定义一个抽象类完全相同,但不允许提供接口中任何成员的实现方式,一般情况下,接口只能包含方法,属性,索引器和事件的声明(抽象方法)。

    接口不能有构造函数,也不能有字段,接口也不允许运算符重载。

    接口定义中不允许声明成员的修饰符,接口成员都是公有的。

    接口可以彼此继承,其方式和类的继承方式相同。

namespace c_sharp_001

{

    interface IFly

    {

        void Fly1();//不可用修饰符修饰,默认public

        void Fly2();

    }

}

namespace c_sharp_001

{

    interface IA

    {

        void MethodA();

    }

}

namespace c_sharp_001

{

    interface IB:IA

    {

        void MethodB();

    }

}

namespace c_sharp_001

{

    class Bird : IFly,IB//类中要实现其继承的接口中的所有方法

    {

        public void Fly1()

        {

        }

        public void Fly2()

        {

        }

        public void MethodA()//IB继承自IA的方法

        {

        }

        public void MethodB()//IB中的方法

        {

        }

    }

}

猜你喜欢

转载自blog.csdn.net/BreakingDawn0/article/details/102759824