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();
        }
    }

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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏blackheart的专栏

[C#2] 5-迭代器

1.枚举数 枚举数是循环访问其关联集合的对象。它可被视作指向集合中任何元素的可移动的指针。 一个枚举数只能与一个集合关联,但一个集合可以具有多个枚举数。C#的f...

1975
来自专栏峰会SaaS大佬云集

C#学习---基础入门(二)

静态常量:const  不能被修改 ,与static变量相同,可以直接通过类名调用。

1204
来自专栏java技术学习之道

7大经典的排序算法总结实现

1426
来自专栏nnngu

算法04 七大排序之:归并排序

上一篇总结了直接插入排序和希尔排序,这一篇要总结的是归并排序,这也是七大排序的最后一种排序算法。 首先来看一下归并排序(Merge Sort) 的基本原理。它的...

36010
来自专栏noteless

[二] java8 函数式接口详解 函数接口详解 lambda表达式 匿名函数 方法引用使用含义 函数式接口实例 如何定义函数式接口

        比如接收双参数的,有 Bi 前缀, 比如 BiConsumer<T,U>, BiFunction<T,U,R> ;

2183
来自专栏Albert陈凯

2018-10-19 分分钟教会你使用Lambda表达式

Java 中使用 Lambda表达式 Lambda的作用 Lambda表达式的作用主要是用来简化接口的创建,interface。 需要注意的是: 1.任...

1216
来自专栏blackheart的专栏

[C#1] 6-方法

1.实例构造器[.ctor] 默认情况下,对于引用类型,如果我们没有显示的定义实例构造器,则C#编译器会为我们定义一个无参的公有实例构造器。 一个类的实例构造器...

2015
来自专栏java一日一条

Java下static关键字用法详解

  本文章介绍了java下static关键字的用法,大部分内容摘自原作者,在此学习并分享给大家。

1713
来自专栏郭耀华‘s Blog

Java.lang.Comparable接口和Java.util.Comparator接口的区别

Java的Comparator和Comparable当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方...

3088
来自专栏python3

python 集合

说明: 拿list_1每一个元素去list_2中查找,如果有,直接忽略,否则就直接输出。

1212

扫码关注云+社区