C#中的面向对象编程

C#中的面向对象编程是指3个基本特征:封装、继承、多态。

封装:

封装是指把类的内部数据隐藏起来,不让对象直接进行操作。C#中可用属性来对类内部的状态进行操作,使用public、private、protected、internal等关键词来实现。

1.为何要封装

当类的内部数据没有被封装时,若把字段定义为公共字段,则外部对象可以对内部数据进行任意的操作,很可能导致不当的操作结果。例如:

public class Person
{
    public int age;
    public string name;
}
class Program
{
    static void Main(string[] args)
    {
        Person person = new Person();
        person.name = "张三";
        person.age = -2;
        Console.Read();
    }
}

以上代码中,虽然程序可以正常运行,但是逻辑上有问题,因为人的年龄不可能为负。
这就是外部对象对类内部数据进行任意操作的产生的不当结果。为了避免这种情况,可以将类进行封装。

2.如何封装

C#提供了属性机制来对私有字段数据进行间接的操作,并且可以在属性中加入判断机制来避免逻辑错误。因此,在面向对象编程中,应更多的定义私有字段。例如:

public class Person
{
    //定义私有字段
    private int age;
    private string name;
    //定义公有属性
    public int Age
    {
        get
        {
            return age;
        }
        set
        {
            //加入逻辑判定
            if(value < 0 || value > 100)
            {
                throw(new ArgumentOutOfRangeException("AgeIntPropery", value, "年龄必须在0到100之间"));
            }
            age = value;
        }
    }

    public string Name
    {
        get{return name;}
        set{name = value;}
    }
}
class Program
{
    static void Main(string[] args)
    {
        Person person = new Person();
        person.Name = "张三";
        person.Age = 20; //设置了一个合理的值
        Console.WriteLine(person.Name);
        Console.WriteLine(person.Age);
    }
}

以上代码中,person.Age设置为一个不合理的数(小于0,大于100)时,执行时程序会中断,并抛出ArgumentOutOfRangeException。
ArgumentOutOfRangeException

继承:

在C#中,一个类可以继承另一个已有类(密封的除外),被继承的类称为基类(或父类),继承的类称为派生类(或子类),子类将获得基类除构造函数和析构函数以外的所有成员。此外,静态类是密封类,不能被继承。

1.什么是继承

简单来说,一个类定义了一些方法、属性等,若另一个类也要定义同样的方法、属性等,可以直接通过继承的方式来获得基类的所有方法、属性等,避免了代码重复。并且同时,子类可以定义另外一些自有的方法、属性等。这就好比男人女人都先拥有人的属性,然后还可以定义自有的属性。

2.如何继承

在C#中,子类仅支持派生于一个基类,也就是一个子类仅可有一个父类。但一个父类可以有多个子类。这些子类都继承父类相同的成员。C#中继承的方法为:

//定义一个基类
public class Father
{
    //基类成员定义
}
//子类继承父类
public class ChildA : Father
{
    //子类自有成员定义
}
public class ChildB : Father
{
    //子类自有成员定义
}

需要注意的是,子类并不能对父类的私有成员进行直接访问,它只可对保护成员和公有成员进行访问。但是子类会继承基类的私有成员,子类可以通过调用公有或保护方法间接的对自有成员进行访问。

密封类:

上面说到,一个类可以继承另一个除密封类以外的类。所以,若不想被继承,则可以使用sealed关键字将其定义为密封类。方法为:

public sealed class SealedClass
{
    //密封类成员定义
}

若有类要继承该密封类时,编译器会报错。

子类的初始化顺序:

上面说到,子类不会继承基类的构造函数和析构函数。但是当子类进行初始化时,任然会调用基类的构造函数。
初始化顺序为:
①初始化子类的实例字段;
②调用基类的构造函数,若没有指明基类,则调用System.Object的构造函数;
③调用子类的构造函数;
例如:

class Program
{
    static void Main(string[] args)
    {
        //初始化子类实例
        Child child = new Child();
        //调用子类方法
        child.Print();
        Console.Read();
    }
}
//创建一个父类Father
public class Father
{
    //初始化它的实例字段;
    private string name = "张三";
    //定义一个方法输出实例字段
    public void Print()
    {
        Console.WriteLine(name);
    }
    //调用基类构造函数
    public Father()
    {
        Console.WriteLine("基类构造函数被调用了");
    }
}
//创建一个子类Child
public class Child : Father
{
    //初始化它的实例字段
    private int Age = 3;
    //定义一个方法输出实例字段
    public void Print()
    {
        Console.WriteLine(Age);
    }
    //调用子类构造函数
    public Child()
    {
        Console.WriteLine("子类构造函数被调用了");
    }
}

运行以上代码,结果为:
运行结果
需要注意,运行结果显示,初始化子类实例时,先执行了基类的构造函数,再执行了子类的构造函数,最后执行了子类的方法。并未执行基类的方法,且子类执行顺序并不是按代码顺序先执行方法再执行构造函数。因此可见总是要并先要执行父类的构造函数,第二再执行子类的构造函数。

多态:

简单来说,如果一个子类继承了一个基类,则获得了基类中的某些行为(如方法、属性等),但是如果子类想改变继承的方法,就需要覆写基类的方法,这种技术就称为多态。

1.用virtual和override关键字实现

基类成员声明为virtual(称为虚方法)时,子类可以重写,如果想改变虚方法的实现行为,要使用override关键字。例如:

    class Program
    {
        static void Main(string[] args)
        {
            //初始化子类实例
            Son son = new Son();
            Daughter daughter = new Daughter();

     //调用子类的方法
            son.Age();
            daughter.Age();
            Console.Read();
        }
    }
    public class Father
    {
        //定义输出年龄的方法
        public virtual void Age()
        {
            Console.WriteLine("父亲的年龄为50岁");
        }
    }
    public class Son : Father
    {
        //覆写输出年龄的方法
        public override void Age()
        {
            //调用基类的方法
            base.Age();
            Console.WriteLine("儿子的年龄为20岁");
        }
    }
    public class Daughter : Father 
    {
        public override void Age()
        {
            //base.Age();不调用基类的方法
            Console.WriteLine("女儿的年龄为18岁");
        }
    }

运行以上代码,结果为:
运行结果
由此可见,子类继承了基类虚方法后对虚方法进行了覆写,在儿子类中覆写时调用了基类方法,因此运行结果仍然有基类方法。女儿类中覆写时未调用基类方法,运行结果则无基类方法。
可见,多态的精髓是相同类型的对象调用相同的方法却表现了不同的行为

2.用abstract关键字防止创建类的实例

用1实现多态时会存在一个问题,即基类可以通过new操作符创建基类的实例。可有的情况下,基类是一个抽象概念(如:人、动物、天气),我们希望避免创建这种抽象的实例。此时可用abstract关键字来防止在代码种直接创建这样的实例,然后仍然可用1实现多态。例如:

public abstract class Wether
{
    public virtual void Print()
    {
        Console.WriteLine("天气状况为:");
    }
}
public class Rain : Wether
{
    public override void Print()
    {
        base.Print();
        Console.WriteLine("雨");
    }
}
public class Sun : Wether
{
    public override void Print()
    {
        base.Print();
        Console.WriteLine("晴");
    }
}
class Program
{
    static void Main(string[] args)
    {
        Rain rain = new Rain();
        Sun sun = new Sun();
        //无法创建基类的实例
        //Wether wether = new Wether(); 

        rain.Print();
        sun.Print();
        Console.Read();
    }
}

运行以上代码,结果为:
运行结果
若将Main中的创建基类实例的注释取消,则会报错:
编译报错

3.用sealed关键字防止子类被覆写

前面讲到可以用sealed关键字来密封类,同理,可以用sealed关键字来密封子类方法防止子类被重写。例如:

public abstract class Parent
{
    public virtual void Marry()
    {
        Console.WriteLine("父母已经结婚25年");
    }
}
public class Son : Parent
{
    //用sealed关键字防止Son类被覆写
    public sealed override void Marry()
    {
        base.Marry();
        Console.WriteLine("儿子已经结婚2年");
    }
}

以上代码的类Son无法被覆写,若添加以下代码:

public class GrandSon : Son
{
    public override void Marry()
    {
        base.Marry();
        Console.WriteLine("孙子还小,不能结婚");
    }
}

则会显示错误信息:无法对密封成员进行复写

4.用new关键字隐藏基类成员

有的情况下,子类需要定义与基类相同名字的成员,此时可以用new关键字将基类成员隐藏起来。若不使用new关键字,则会报错。例如:

public class Father
{
    public void Name()
    {
        Console.WriteLine("爸爸叫大明");
    }
}
public class Son : Father
{
    //子类中仍然想定义一个叫Name的方法,使用new关键字
    public new void Name()
    {
        Console.WriteLine("儿子叫小明");
    }
}

如果此时仍然想要访问基类的成员,可使用强制类型转换,把子类强制转换成基类类型,从而访问隐藏的基类成员。例如:

class Program

{

    static void Main(string[] args)

    {

        Son son = new Son();

        son.Name();



        //调用基类的Name方法

        ((Father)son).Name();

        Console.Read();

    }

}

以上代码,结果为:

运行结果

所有类的父类:System.Object

C#中,所有的类都派生自System.Object类。如果类没有指定任何的基类,则编译器就自动吧Object当作它的基类。
详细信息请参考:Object类(System)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值