c#学习笔记-------------继承和抽象类

学习笔记 / 2023-08-07 / 原文

类继承

通过继承我们可以定义一个新类,新类纳入一个已经声明的类并进行扩展

继承是面向对象的编程的一种基本特性。 借助继承,能够定义可重用(继承)、扩展或修改父类行为的子类。

成员被继承的类称为基类

继承基类成员的类称为派生类

C# 和 .NET 只支持单一继承。 也就是说,类只能继承自一个类。

不过,继承是可传递的。

这样一来,就可以为一组类型定义继承层次结构。

换言之,类型 D 可继承自类型 C,其中类型 C 继承自类型 B,类型 B 又继承自基类类型 A

由于继承是可传递的,因此类型 D 继承了类型 A 的成员。

并非所有基类成员都可供派生类继承。 以下成员无法继承:

  • 静态构造函数:用于初始化类的静态数据。

  • 实例构造函数:在创建类的新实例时调用。 每个类都必须定义自己的构造函数。

  • 终结器:由运行时的垃圾回收器调用,用于销毁类实例。

虽然基类的其他所有成员都可供派生类继承,但这些成员是否可见取决于它们的可访问性

修饰符

修饰符,⽤来类型或者成员的关键字。

修饰符可以指定⽅法的可⻅性。

  1. public: 同⼀程序集(DLL或EXE)中的任何其他代码或引⽤该程序集的其他程序集都可 以访问该类型或成员。
  2. private: 只有同⼀类或结构中的代码可以访问该类型或成员。
  3. protected: 只有同⼀类或结构或者此类的派⽣类中的代码才可以访问该类型或成员。 internal: 同⼀程序集中的任何代码都可以访问该类型或成员,但的代码不可以。
  4. protected internal: 在⼀程序集中,protected internal体现的是internal的性质;在其 他程序集中,protected internal体现的是protected的性质。

public 和private修饰字段和⽅法的时候,表⽰该字段或者⽅法能不能通过对象去访问,

只 有public的才可以通过对象访问,private(私有的)只能在类模板内部访问。

protected 保护的,当没有继承的时候,它的作⽤和private是⼀样的,

当有继承的时候, protected表⽰可以被⼦类访问的字段或者⽅法

 

 

 保护级别

namespace Classes;
 
//只有在基类中嵌套的派生类中,私有成员才可见
public class A
{
    private int _value = 10;
    
    public class B : A
    {
        public int GetValue()
        {
            Console.WriteLine("--------B--------");
            return _value;           
        }
    }
}
 
public class C : A
{
    public int GetValue()
    {
//在这里会报错Compiler Error CS0122,
// CS0122:“"A._value" 不可访问,因为它具有一定的保护级别
        return _value;
    }
}

 

受保护成员仅在派生类中可见

namespace Classes;
 
//只有在基类中嵌套的派生类中,私有成员才可见
public class A
{
    protected int _value = 10;
    
    public class B : A
    {
        public int GetValue()
        {
            Console.WriteLine("--------B--------");
            return _value;           
        }
    }
}
 
public class C : A
{
    public int GetValue()
    {
        Console.WriteLine("--------C--------");
        return _value;
    }
}
 
public class AccessExample
{
    public static void Main(string[] args)
    {
        //A.B 是派生自 A 的嵌套类,而 C 则派生自 A。 私有 A._value 字段在 A.B 中可见。
        var b = new A.B();
        Console.WriteLine(b.GetValue());
 
        var c = new C();
        Console.WriteLine(c.GetValue());
    }
}
// The example displays the following output:
//--------B--------
//10
//--------C--------
//10

公共成员在派生类中可见,并且属于派生类的公共接口。

public class A
{
    public void Method1()
    {
        // Method implementation.
    }
}

public class B : A
{ }

public class Example
{
    public static void Main()
    {
        B b = new ();
        b.Method1();
    }
}

 

this和base关键字

this(base)作⽤:

  1. IDE给提⽰
  2. 区分局部变量和字段

this可以访问当前类中定义的字段,属性和⽅法,有没有this都可以访问,

有this可以让IDE-VS编译器给出提⽰,另外当⽅法的参数跟字段重名的时候,

使⽤this可以表明访问的是类 中的字段,base可以调⽤⽗类中的公有⽅法和字段,

有没有base都可以访问,但是加上 base.IED⼯具会给出提⽰,

把所有可以调⽤的字段和⽅法罗列出来⽅便选择

隐式继承

.NET 类型系统中的所有类型除了可以通过单一继承进行继承之外,还可以隐式继承自 Object 或其派生的类型。 Object 的常用功能可用于任何类型。

为了说明隐式继承的具体含义,让我们来定义一个新类 SimpleClass,这只是一个空类定义:

然后可以使用反射(便于检查类型的元数据,从而获取此类型的相关信息),获取 SimpleClass 类型的成员列表。 尽管没有在 SimpleClass 类中定义任何成员,但示例输出表明它实际上有九个成员。 这些成员的其中之一是由 C# 编译器自动为 SimpleClass 类型提供的无参数(或默认)构造函数。 剩余八个是 Object(.NET 类型系统中的所有类和接口最终隐式继承自的类型)的成员。

namespace Classes
{
    public class SimpleClass
    { }
}
namespace Classes;
 
using System.Reflection;
 
public class SimpleClassExample
{
    public static void Main()
    {
        Type t = typeof(SimpleClass);
        BindingFlags flags = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public |
                             BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
        MemberInfo[] members = t.GetMembers(flags);
        Console.WriteLine($"Type {t.Name} has {members.Length} members: ");
        //SimpleClass有九个成员:
        foreach (MemberInfo member in members)
        {
            string access = "";
            string stat = "";
            var method = member as MethodBase;
            if (method != null)
            {
                if (method.IsPublic)
                    access = " Public";
                else if (method.IsPrivate)
                    access = " Private";
                else if (method.IsFamily)
                    access = " Protected";
                else if (method.IsAssembly)
                    access = " Internal";
                else if (method.IsFamilyOrAssembly)
                    access = " Protected Internal ";
                if (method.IsStatic)
                    stat = " Static";
            }
            string output = $"{member.Name} ({member.MemberType}): {access}{stat}, Declared by {member.DeclaringType}";
            Console.WriteLine(output);
            //公共 GetType 方法:返回表示 SimpleClass 类型的 Type 对象。
            //GetType(Method):  Public, Declared by System.Object
 
            //受保护 MemberwiseClone 方法:创建当前对象的浅表复制。
            //MemberwiseClone(Method):  Protected, Declared by System.Object
 
            //受保护 Finalize 方法:用于在垃圾回收器回收对象的内存之前释放非托管资源。
            //Finalize(Method):  Protected, Declared by System.Object
 
            //公共 ToString 方法将 SimpleClass 对象转换为字符串表示形式,返回完全限定的类型名称。 在这种情况下,ToString 方法返回字符串“SimpleClass”。
            //ToString(Method):  Public, Declared by System.Object   
 
            //公共实例 Equals(Object) 方法、公共静态 Equals(Object, Object) 方法和公共静态 ReferenceEquals(Object, Object) 方法。 默认情况下,这三个方法测试的是引用相等性;也就是说,两个对象变量必须引用同一个对象,才算相等。
            //Equals(Method):  Public, Declared by System.Object
            //Equals(Method):  Public Static, Declared by System.Object
            //ReferenceEquals(Method):  Public Static, Declared by System.Object
 
            //公共GetHashCode 方法:计算允许在经哈希处理的集合中使用类型实例的值。
            //GetHashCode(Method):  Public, Declared by System.Object
            //.ctor(Constructor):  Public, Declared by Classes.SimpleClass
        }
    }
}

由于是隐式继承,因此可以调用 SimpleClass 对象中任何继承的成员,就像它实际上是 SimpleClass 类中定义的成员一样。

抽象类

abstract 修饰符可以和类、方法、属性、索引器及事件一起使用。在类声明中使用 abstract 修饰符以指示类只能是其他类的基类。

 

抽象类特性:

  • 抽象类不能实例化。

  • 抽象类可以包含抽象方法和抽象访问器。

  • 不能用 sealed 修饰符修改抽象类,这意味着该类不能被继承。

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

在方法或属性声明中使用 abstract 修饰符以指示此方法或属性不包含实现。

 

抽象方法特性:

  • 抽象方法是隐式的 virtual 方法。

  • 只允许在抽象类中使用抽象方法声明。

  • 因为抽象方法声明不提供实实现,所以没有方法体;方法声明只是以一个分号结束,

 

抽象方法在签名后没有大括号 ({ })

例如:

  • public abstract void MyMethod(); 

  • 实现由 overriding 方法提供,它是非抽象类的成员。

  • 在抽象方法声明中使用 static 或 virtual 修饰符是错误的。

 

除了在声明和调用语法上不同外,抽象属性的行为与抽象方法一样。

  • 在静态属性上使用 abstract 修饰符是错误的。

  • 在派生类中,通过包括使用 override 修饰符的属性声明可以重写抽象的继承属性。

抽象类必须为所有接口成员提供实现。

例子:

抽象基类

using System;
using System.Collections.Generic;
using System.Text;

namespace _09_抽象类
{
    public abstract class Enemy
    {
        private int hp;
        private int speed;

        public void Move()
        {
            Console.WriteLine("Move");
        }
        public abstract void Attack();

    }
}

子类继承需要实现其抽象方法

using System;
using System.Collections.Generic;
using System.Text;

namespace _09_抽象类
{
    public class Boss : Enemy
    {
        public override void Attack()
        {
            Console.WriteLine("Boss进行攻击");
        }
    }
}