C#基础知识(base、this、new、override、abstract、virtual、static)

前言

本文主要来讲解一下C#中,自己觉得掌握的不怎么样或者用的不多,不太熟悉的关键字,主要包括base、this、new、override、abstract、virtual以及针对static字段和static构造函数之间的执行问题。

base关键字

base 关键字用于在派生类中实现对基类公有或者受保护成员的访问,但是只局限在构造函数、实例方法和实例属性访问器中:

  • 调用基类上已被其他方法重写的方法。

    public class Father
    {
        public virtual void Say()
        {
            Console.WriteLine("Father Say");
        }
    }

    public class Son : Father
    {
        public override void Say()
        {
            base.Say();
            Console.WriteLine("Son Say");
        }
    }

  • 指定创建派生类实例时应调用的基类构造函数。

    public class Father
    {
        public string Name { get; set; }

        public Father()
        {
            Name = "Father";
        }
    }

    public class Son : Father
    {
        public Son()
            : base()
        { 

        }
    }

从静态方法中使用 base 关键字是错误的。

this关键字

其用于引用类的当前实例,也包括继承而来的方法,通常可以隐藏this:

  • 限定被相似的名称隐藏的成员

    public class Person
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public Person(string Name, int Age)
        {
            this.Name = Name;
            this.Age = Age;
        }
    }

  • 将对象作为参数传递到其他方法

    public class Person
    {
        public string Name { get; set; }

        public int Age { get; set; }

        public Person(string Name, int Age)
        {
            this.Name = Name;
            this.Age = Age;
        }
        public void CallTest(Person person)
        {
            Console.WriteLine(person.Name+person.Age);
        }

        public void Call()
        {
            CallTest(this);
        }
    }

  • 声明索引器

    public class Person
    {
        string[] PersonList = new string[10];
        public string this[int param]
        {
            get { return PersonList[param]; }
            set { PersonList[param] = value; }
        }

new关键字

一、new运算符

1、new一个class时,new完成了以下两个方面的内容:一是调用new class命令来为实例在托管堆中分配内存;二是调用构造函数来实现对象初始化。

2、new一个struct时,new运算符用于调用其带构造函数,完成实例的初始化。

3、new一个int时,new运算符用于初始化其值为0。

4、 new运算符不可重载。

5、new分配内存失败,将引发OutOfMemoryException异常。

二、new修饰符

new 关键字可以显式隐藏从基类继承的成员。 隐藏继承的成员时,该成员的派生版本将替换基类版本。 虽然可以在不使用 new 修饰符的情况下隐藏成员,但会生成警告。 如果使

用 new 显式隐藏成员,则会取消此警告,并记录要替换为派生版本这一事实。

在子类中用 new 关键字修饰 定义的与父类中同名的方法,叫覆盖。 覆盖不会改变父类方法的功能。

    public class A
    {
        public virtual void Test()
        {
            Console.WriteLine("A.Test()");
        }
    }

    public class B : A
    {
        public new void Test()
        {
            Console.WriteLine("B.Test()");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.Test();
            B b = new B();
            b.Test();
            A c = new B();
            c.Test();
            Console.ReadLine();
        }
    }
}

当用子类创建父类的时候,如 A c = new B(),覆盖不会改变父类的功能,仍然调用父类功能。(和override有区别,下面进行讲解)

三、new 约束

new 约束指定泛型类声明中的任何类型参数都必须有公共的无参数构造函数。 如果要使用 new 约束,则该类型不能为抽象类型。

当与其他约束一起使用时,new() 约束必须最后指定:

    public class ClassA<T>where T : IComparable, new()
    {
        ////
    }

override关键字

要扩展或修改继承的方法、属性、索引器或事件的抽象实现或虚实现,必须使用 override 修饰符。

由 override 声明重写的方法称为重写基方法。 重写的基方法必须与 override 方法具有相同的签名。

不能重写非虚方法或静态方法。 重写的基方法必须是 virtual、abstract 或 override 的。

用关键字 virtual 修饰的方法,叫虚方法。可以在子类中用override 声明同名的方法,这叫“重写”。相应的没有用virtual修饰的方法,我们叫它实方法。 重写会改变父类方法的功能。

    public class A
    {
        public virtual void Test()
        {
            Console.WriteLine("A.Test()");
        }
    }

    public class B : A
    {
        public override void Test()
        {
            Console.WriteLine("B.Test()");
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            a.Test();
            B b = new B();
            b.Test();
            A c = new B();
            c.Test();
            Console.ReadLine();
        }
    }

new 和override

1、 不管是重写还是覆盖都不会影响父类自身的功能。

2、当用子类创建父类的时候,如 A c = new B(),重写会改变父类的功能,即调用子类的功能;而覆盖不会,仍然调用父类功能。

3、虚方法、实方法都可以被覆盖(new),抽象方法,接口 不可以。

4、抽象方法,接口,标记为virtual的方法可以被重写(override),实方法不可以。

5、重写使用的频率比较高,实现多态;覆盖用的频率比较低,用于对以前无法修改的类进行继承的时候。

abstract关键字

针对abstract关键字暂时总结了以下五点:

1.用关键字abstract定义的类即为抽象类,且只能作为基类,也不能被实例化。
2.用abstract定义的类不一定包含抽象方法,也可以包含非抽象方法。
3.abstract定义的方法一定包含在抽象类中。
4.抽象类不能定义为密封类(sealed),抽象方法不能使用virtual、static、private修饰符
5.如果派生类没有实现所有的抽象方法,则该派生类也必须声明为抽象类。

virtual关键字

Virtual方法(虚方法)

virtual 关键字用于在基类中修饰方法。virtual的使用会有两种情况:

情况1:在基类中定义了virtual方法,但在派生类中没有重写该虚方法。那么在对派生类实例的调用中,该虚方法使用的是基类定义的方法。

情况2:在基类中定义了virtual方法,然后在派生类中使用override重写该方法。那么在对派生类实例的调用中,该虚方法使用的是派生重写的方法。

static字段和static构造函数

主要来说明执行的顺序:

  1、编译器在编译的时候,先分析所需要的静态字段,如果这些静态字段所在的类有静态的构造函数,那么就会忽略字段的初始化;如果没有静态的构造函数,那么就会对静态字段进行初始化。

  2、如果存在多个静态类,那么初始化的静态成员的顺序会根据引用的顺序,先引用到的先进行初始化,但如果类的静态成员的初始化依赖于其他类的静态成员,则会先初始化被依赖的静态成员。

  3、而带有静态构造函数的类的静态字段,只有在引用到的时候才进行初始化。

下面我们来看两个简单的小例子:

    public class A
    {
        public static int X = B.Y+1;
        static A() { }
    }

    public class B
    {
        public static int Y=A.X+1;
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("A.X={0},B.Y={1}",A.X,B.Y);
            Console.ReadLine();
        }
    }

结果如何呢?再来看第二个小例子:

    public class A
    {
        public static int X = B.Y+1;
    }

    public class B
    {
        public static int Y=A.X+1;
        static B() { }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("A.X={0},B.Y={1}",A.X,B.Y);
            Console.ReadLine();
        }
    }

对比一下,这两个例子,如果你想的和你执行后的结果一致,那说明你应该已经明白它们的执行顺序了。

abstract、virtual、override 和 new 是在类的继承关系中常用的四个修饰方法的关键字,在此略作总结。

1. 常用的中文名:abstract 抽象方法,virtual 虚方法,override 覆盖基类方法,new 隐藏基类方法,override 和 new 有时都叫重写基类方法。

2. 适用场合:abstract 和 virtual 用在基类(父类)中;override 和 new 用在派生类(子类)中。

3. 具体概念:

abstract 抽象方法,是空方法,没有方法体,派生类必须以 override 实现此方法。

virtual 虚方法,若希望或预料到基类的这个方法在将来的派生类中会被重写(override 或 new),则此方法必须被声明为 virtual。

override 重写继承自基类的 virtural 方法,可以理解为拆掉老房子,在原址上建新房子,老房子再也找不到了(除非显式地用 base. 调用基类方法)。

new 隐藏继承自基类的 virtual 方法,老房子还留着,在旁边盖个新房,想住新房住新房(作为派生类对象调用),想住老房住老房(作为基类对象调用)。

当派生类中出现与基类同名的方法,而此方法前面未加 override 或 new 修饰符时,编译器会报警告,但不报错,真正执行时等同于加了 new。

4. abstract 和 virtual 的区别:abstract 方法还没实现,连累着基类也不能被实例化,除了作为一种规则或符号外没啥用;virtual 则比较好,派生类想重写就重写,不想重写就吃老子的。而且继承再好也是少用为妙,继承层次越少越好,派生类新扩展的功能越少越好,virtual 深合此意。

5. override 和 new 的区别:当派生类对象作为基类类型使用时,override 的执行派生类方法,new 的执行基类方法。如果作为派生类类型调用,则都是执行 override 或 new 之后的。

演示 override 和 new 区别的例子:


// Define the base class
class Car
{
   public virtual void DescribeCar()
   {
       System.Console.WriteLine("Four wheels and an engine.");
   }
}

// Define the derived classes
class ConvertibleCar : Car
{
   public new virtual void DescribeCar()
   {
       base.DescribeCar();
       System.Console.WriteLine("A roof that opens up.");
   }
}
class Minivan : Car
{
   public override void DescribeCar()
   {
       base.DescribeCar();
       System.Console.WriteLine("Carries seven people.");
   }
}
public static void TestCars1()
{
   Car car1 = new Car();
   car1.DescribeCar();
   System.Console.WriteLine("----------");

ConvertibleCar car2 = new ConvertibleCar();
   car2.DescribeCar();
   System.Console.WriteLine("----------");

Minivan car3 = new Minivan();
   car3.DescribeCar();

System.Console.WriteLine("----------");

}

输出类似如下所示:

Four wheels and an engine.

----------

Four wheels and an engine.

A roof that opens up.

----------

Four wheels and an engine.

Carries seven people.

----------

但是,如果我们声明一个从 Car 基类派生的对象的数组。此数组能够存储 Car、ConvertibleCar 和 Minivan 对象,如下所示:


public static void TestCars2()
{
    Car[] cars = new Car[3];
    cars[0] = new Car();
    cars[1] = new ConvertibleCar();

cars[2] = new Minivan();

}

然后用一个 foreach 循环来访问该数组中包含的每个 Car 对象,并调用 DescribeCar 方法,如下所示:


foreach (Car vehicle in cars)
{
  System.Console.WriteLine("Car object: " + vehicle.GetType());
  vehicle.DescribeCar();

System.Console.WriteLine("----------");

}

此循环的输出如下所示:

Car object: YourApplication.Car

Four wheels and an engine.

----------

Car object: YourApplication.ConvertibleCar

Four wheels and an engine.

----------

Car object: YourApplication.Minivan

Four wheels and an engine.

Carries seven people.

----------

注意,ConvertibleCar 的说明可能与您的预期不同。由于使用了 new 关键字来定义此方法,所调用的不是派生类方法,而是基类方法。Minivan 对象正确地调用重写方法,并产生预期的结果。

时间: 2024-10-25 22:41:07

C#基础知识(base、this、new、override、abstract、virtual、static)的相关文章

C#基础知识之base关键字

前言 其实很早就想写关于C#基础,总是自己给自己找借口,或者去网上搜搜看看,现在想想觉得自己有点懒惰...  作为开发人员,基础知识是重中之重的,只有巩固.理解.实践才能提高自身的技能.同时也希望通过这个系列,能够帮助需要巩固基础的人. base关键字 调用基类已被派生类重写的方法 public class Father { public virtual void Say() { Console.WriteLine("Father Say"); } } public class Son

Java 基础知识之-------------Override 与 Overload

override可以翻译为覆盖,从字面就可以知道,它是覆盖了一个方法并且对其重写,以求达到不同的作用.对我们来说最熟悉的覆盖就是对接口方法的实现,在接口中一般只是对方法进行了声明,而我们在实现时,就需要实现接口声明的所有方法.除了这个典型的用法以外,我们在继承中也可能会在子类覆盖父类中的方法.在覆盖要注意以下的几点: 1.覆盖的方法的标志必须要和被覆盖的方法的标志完全匹配,才能达到覆盖的效果: 2.覆盖的方法的返回值必须和被覆盖的方法的返回一致: 3.覆盖的方法所抛出的异常必须和被覆盖方法的所抛

C# 派生,virtual,sealed,new ,override,abstract,base

/*extends,sealed,override,new,virtual,abstract,base * 继承,密闭类,重写(覆盖),隐藏(hide),虚(可替换),抽象,调用父类成员 * 重点:多态性(多种形态polymorphism):相同签名的成员在不同的类中有不同的实现,就被成为多态性,也只同一个签名有多个实现: * 派生/继承:对基类进行特化,包含附加的尘缘或对基类成员进行自定义,建立了“属于”这种关系. * 派生类型/子类型:继承了较常规类型的成员的特化类型 * 基/超/父类型:其

20151024_004_C#基础知识(C#中的访问修饰符,继承,new关键字,里氏转换,is 和 as,多态,序列化与反序列化)

1:C#中的访问修饰符 public: 公共成员,完全公开,没有访问限制. private: 私有的,只能在当前类的内部访问. protected: 受保护的,只能在当前类的内部以及该类的子类中访问. internal: 只能在当前项目中访问,在同一个项目中,internal和public的权限是一样的. protected internal: protected + internal权限. 1.1:能够修饰类的访问修饰符只有两个: public / internal 1.2:接口中的成员不允许

Android自定义view教程05--自定义view的基础知识之LayoutInflater详解

前面的教程我们讲了一下android的动画 的使用,尤其是着重讲解了属性动画的使用.那么这章开始我们将会讲一下android 自定义view的一些基础知识.帮助大家理解. 首先我们来关注一下LayoutInflater这个类,经常使用开源控件的人对这个类应该很熟悉了,但是很少有人能把这个类讲明白 用清楚,今天我们就来深挖一下这个类. 首先我们定义一个button.xml 和button2.xml 1 <?xml version="1.0" encoding="utf-8

【RAC】RAC相关基础知识

[RAC]RAC相关基础知识 1.CRS简介    从Oracle 10G开始,oracle引进一套完整的集群管理解决方案—-Cluster-Ready Services,它包括集群连通性.消息和锁.负载管理等框架.从而使得RAC可以脱离第三方集群件,当然,CRS与第三方集群件可以共同使用. (1).CRS进程 CRS主要由三部分组成,三部分都作为守护进程出现 <1>CRSD:资源可用性维护的主要引擎.它用来执行高可用性恢复及管理操作,诸如维护OCR及管理应用资源,它保存着集群的信息状态和OC

《大话设计模式》笔记-基础知识2:面向对象基础知识

本书作者介绍每一种设计模式就是用类图(用的UML类图,其基础知识另文介绍)+面向对象语言小程序+人物对话解释知识点.本文就是介绍<大话设计模式>中所用到面向对象的基础知识(基于C#语言),方便阅读本书代码. 类与实例 (1)对象 一切事物皆为对象,对象就是看到.感觉到.听到.摸到等实实在在的东西.准确地说,对象是一个自包含的实体,用一组可识别的特性和行为来标识.面向对象编程,就是针对对象来进行编程的. (2)类 类就是具有相同的属性和功能的对象的抽象的集合.例子如下 "class&q

Scala学习(1)——Scala基础知识

本文要解决的问题: Spark主要是由Scala语言编写而成的,所以要真正深入了解Spark,必须要熟悉Scala,在此结合阅读<Scala编程>这本书的情况,对Scala语言做一个基本的总结. Scala的优势 (1)简洁 类型推断 函数创建的文法支持 (2)Java互操作性 可重用Java库 可重用Java工具 没有性能惩罚 Scala工作机制 编译成Java字节码 可在任何标准JVM上运行,甚至是一些不规范的JVM上 Scala编译器是Java编译器的作者写的 启动解释器 输入Scala

工作中能用到的基础知识总结(二)

简介 继承.封装和多态是面向对象编程的重要特性.要想运用好,就必须熟悉这三种特性,本篇说说我对封装.继承和多态相关的知识总结. 知识点 一.访问修饰符 C#中类及类型成员修饰符有五类:public,private,protected,internal,protected  internal. public:类及类型成员的修饰符(任何地方该类都可以被访问到): private:类型成员的修饰符(只能在同一个类或方法中使用,即使是类的实例也不能访问它的私有成员): protected:类型成员的修饰