【从零开始入门unity游戏开发之——C#篇26】C#面向对象动态多态——接口(Interface)、接口里氏替换原则、密封方法(`sealed` )

文章目录

一、接口(Interface

在 C# 中,接口(Interface) 是一种定义契约的方式,它指定了类或者结构体应该提供哪些方法、属性、事件或索引器,但是不提供实现细节。接口为不同的类提供了一个共同的访问方式,使得它们能够通过接口进行交互。

接口与抽象类相似,都用于定义行为,但两者有一些重要的区别。接口只能包含方法的声明(没有实现),而抽象类可以包含方法的实现。

1、接口的定义

接口使用 interface 关键字定义,接口中的成员默认为 public,且不能包含字段(只能包含方法、属性、事件和索引器)。此外,接口不能包含构造函数和静态成员。

接口命名规范:帕斯卡命名前面加个I

接口的基本语法:

csharp 复制代码
public interface IAnimal
{
    // 接口中的方法声明,不包含实现
    void Speak();

    // 接口中的属性声明
    string Name { get; set; }
}

2、接口的实现

类或者结构体使用 : interface 来实现一个接口,并必须实现接口中声明的所有成员。

实现接口:

csharp 复制代码
using System;

public interface IAnimal
{
    void Speak();
    string Name { get; set; }
}

public class Dog : IAnimal
{
    public string Name { get; set; }

    public Dog(string name)
    {
        Name = name;
    }

    // 实现接口中的 Speak 方法
    public void Speak()
    {
        Console.WriteLine($"{Name} barks.");
    }
}

public class Cat : IAnimal
{
    public string Name { get; set; }

    public Cat(string name)
    {
        Name = name;
    }

    // 实现接口中的 Speak 方法
    public void Speak()
    {
        Console.WriteLine($"{Name} meows.");
    }
}

class Program
{
    static void Main()
    {
        IAnimal dog = new Dog("Buddy");
        IAnimal cat = new Cat("Whiskers");

        dog.Speak(); // 输出:Buddy barks.
        cat.Speak(); // 输出:Whiskers meows.
    }
}

在上面的代码中,IAnimal 是一个接口,DogCat 类实现了该接口。每个类提供了对接口成员 Speak 方法的具体实现。

3、接口的特性

  1. 接口中的成员不能包含实现

    • 接口定义了方法签名和属性声明,但不包括方法体和属性的实现。接口只描述了功能,而不涉及实现。
  2. 类可以实现多个接口

    • 在 C# 中,一个类可以实现多个接口。这使得 C# 支持多重继承(通过接口实现),弥补了单继承的不足。
    csharp 复制代码
    public interface IAnimal
    {
        void Speak();
    }
    
    public interface IFlyable
    {
        void Fly();
    }
    
    public class Bird : IAnimal, IFlyable
    {
        public void Speak()
        {
            Console.WriteLine("Bird chirps.");
        }
    
        public void Fly()
        {
            Console.WriteLine("Bird flies.");
        }
    }
  3. 接口成员是隐式 public

    • 接口中的成员默认是 public,并且不能使用 privateprotected 等访问修饰符。这意味着接口中的所有成员都会公开给实现它的类。
  4. 接口不能包含字段和静态成员

    • 接口中不能包含字段、构造函数、静态成员或静态方法,只有成员方法、属性、事件和索引器。
  5. 接口不能包含实现代码

    • 接口本身不提供任何实现,它仅定义类或者结构体应该提供的方法签名。实现接口的类才提供方法的实现。
  6. 接口可以继承其他接口

    • 接口可以继承其他接口,使得接口可以构建更加复杂和灵活的契约。
    csharp 复制代码
    public interface IAnimal
    {
        void Speak();
    }
    
    public interface IFlyable
    {
        void Fly();
    }
    
    public interface IFlyingAnimal : IAnimal, IFlyable
    {
        // 继承自 IAnimal 和 IFlyable
    }
    
    public class Bird : IFlyingAnimal
    {
        public void Speak()
        {
            Console.WriteLine("Bird chirps.");
        }
    
        public void Fly()
        {
            Console.WriteLine("Bird flies.");
        }
    }
  7. 接口与抽象类的区别

    • 继承方式:类可以实现多个接口,但只能继承一个抽象类。
    • 成员实现:抽象类可以包含方法实现,而接口只定义方法签名,不包含实现。
    • 构造函数:抽象类可以有构造函数,而接口不能有构造函数。
    • 字段:抽象类可以包含字段,接口则不能。
  8. 接口可以作为参数类型

    • 接口非常适合用于方法参数类型,以实现松耦合和多态性。
    csharp 复制代码
    public class AnimalSoundPlayer
    {
        public void PlaySound(IAnimal animal)
        {
            animal.Speak();
        }
    }
    
    class Program
    {
        static void Main()
        {
            IAnimal dog = new Dog("Buddy");
            IAnimal cat = new Cat("Whiskers");
    
            AnimalSoundPlayer soundPlayer = new AnimalSoundPlayer();
            soundPlayer.PlaySound(dog);  // 输出:Buddy barks.
            soundPlayer.PlaySound(cat);  // 输出:Whiskers meows.
        }
    }

4、接口的优势

  1. 解耦:通过接口定义契约,使得不同的类之间的实现细节得以解耦。即使某些类的实现发生了变化,只要它们依然遵循接口的契约,外部代码就不需要修改。

  2. 多态:接口提供了多态性。通过接口类型引用不同实现类的对象,使得代码更加灵活和可扩展。

  3. 支持多重继承:C# 不支持类的多重继承,但通过接口可以实现多重继承的效果,一个类可以实现多个接口。

  4. 可扩展性和灵活性:接口可以用于扩展系统而不改变现有代码的结构。如果你想给某个类添加新的功能,只需要定义新的接口并让该类实现即可。

5、小结

  • 接口 提供了一种方式来定义功能的契约或规范,而不涉及具体的实现。
  • 接口可以被多个类实现,使得不同类能够共享相同的行为。
  • 与抽象类不同,接口不包含任何实现代码,而是定义类必须实现的成员。
  • 使用接口能够提高代码的可扩展性和灵活性,尤其适用于定义多个类之间共享的行为。

二、接口也遵循里氏替换原则

里氏替换原则:可以用父类容器装载子类对象

csharp 复制代码
interface IRun { }
class GameObject { }
class Player : GameObject, IRun
{
    public void PlayerAtk()
    {
        Console.WriteLine("玩家攻击");
    }
}
class Monster : GameObject, IRun
{
    public void MonsterAtk()
    {
        Console.WriteLine("怪兽攻击");
    }
}
class Boss : GameObject, IRun
{
    public void BossAtk()
    {
        Console.WriteLine("Boss攻击");
    }
}

调用

csharp 复制代码
IRun player = new Player();
IRun monster = new Monster();
IRun boss = new Boss();

if (player is Player)
{
    (player as Player).PlayerAtk();
}

三、密封方法(sealed

在 C# 中,密封方法sealed method)是指在派生类中无法进一步被重写的方法。密封方法是通过 sealed 关键字来实现的,通常是为了阻止进一步的修改,确保在继承结构中该方法的行为是固定的。

1、密封方法的定义

  • 基本概念 :密封方法是用于防止在子类中重写的方法。它是通过在派生类中使用 sealed 关键字与 override 关键字一起定义的。

  • 使用场景:当你希望某个方法在继承层次结构中保持固定且不能被子类修改时,可以使用密封方法。

2、如何使用密封方法

在 C# 中,密封方法是通过在派生类中重写父类方法时使用 sealed 关键字来标记的。这样,子类就无法再重写这个方法。

示例代码:

csharp 复制代码
using System;

class BaseClass
{
    // 普通虚方法
    public virtual void DoSomething()
    {
        Console.WriteLine("BaseClass: Doing something...");
    }
}

class DerivedClass : BaseClass
{
    // 重写父类的方法
    public override void DoSomething()
    {
        Console.WriteLine("DerivedClass: Doing something...");
    }
}

class FinalClass : DerivedClass
{
    // 密封方法,防止在子类中被重写
    public sealed override void DoSomething()
    {
        Console.WriteLine("FinalClass: Doing something in a sealed way...");
    }
}

class Program
{
    static void Main()
    {
        BaseClass obj = new FinalClass();
        obj.DoSomething();  // 输出: FinalClass: Doing something in a sealed way...
    }
}

3、解释

  • BaseClass 类定义了一个虚方法 DoSomething,该方法允许被子类重写。
  • DerivedClass 类重写了 DoSomething 方法,使得它的行为发生了变化。
  • FinalClass 类重写了 DoSomething 方法,并使用了 sealed 关键字标记它。这样,FinalClass 不能再被任何进一步的派生类重写此方法。

4、为什么要使用密封方法?

  • 控制继承结构:通过密封方法,可以确保某些方法的行为不会被进一步改变。这有助于保持代码的一致性和可预测性,特别是在设计中希望防止继承类修改某些关键功能时。

  • 性能优化:在一些情况下,密封方法可以帮助编译器优化代码,因为密封方法在继承层次中不会被重写,所以编译器可以做更多的优化。

5、注意事项

  • 只能在派生类中密封方法:即必须先在基类中定义为 virtualabstract 方法,然后才能在派生类中使用 sealed 来封闭它。
  • 不能密封非虚方法 :只有虚方法(virtualabstract)才能被密封。普通的非虚方法不能使用 sealed 关键字。

6、总结

密封方法(sealed method)是 C# 中用于防止在子类中重写父类方法的一种机制,通常用于确保某些方法在继承层次结构中保持一致性和固定的行为。在派生类中使用 sealed 来标记一个方法时,后续的类将不能重写此方法,从而保证了该方法的行为不会被进一步改变。


专栏推荐

地址
【从零开始入门unity游戏开发之------C#篇】
【从零开始入门unity游戏开发之------unity篇】
【制作100个Unity游戏】
【推荐100个unity插件】
【实现100个unity特效】
【unity框架开发】

完结

赠人玫瑰,手有余香!如果文章内容对你有所帮助,请不要吝啬你的点赞评论和关注,你的每一次支持都是我不断创作的最大动力。当然如果你发现了文章中存在错误或者有更好的解决方法,也欢迎评论私信告诉我哦!

好了,我是向宇https://xiangyu.blog.csdn.net

一位在小公司默默奋斗的开发者,闲暇之余,边学习边记录分享,站在巨人的肩膀上,通过学习前辈们的经验总是会给我很多帮助和启发!如果你遇到任何问题,也欢迎你评论私信或者加群找我, 虽然有些问题我也不一定会,但是我会查阅各方资料,争取给出最好的建议,希望可以帮助更多想学编程的人,共勉~

相关推荐
代码拯救不了世界几秒前
若依框架中的上传图片后如何实现回显到页面的
java·图片上传·若依
lxyzcm3 分钟前
深入理解C++23的Deducing this特性(上):基础概念与语法详解
开发语言·c++·spring boot·设计模式·c++23
heirui_Oooo3 分钟前
利用两种方式分别实现单例模式(懒汉式、饿汉式)
java·开发语言
天使day11 分钟前
Maven
java·maven
汇匠源13 分钟前
共享无人系统,从出行到生活全面覆盖
java·生活
好开心3341 分钟前
axios的使用
开发语言·前端·javascript·前端框架·html
超龄魔法少女43 分钟前
[Unity] ShaderGraph动态修改Keyword Enum,实现不同效果一键切换
unity·技术美术·shadergraph
又蓝1 小时前
使用 Python 操作 Excel 表格
开发语言·python·excel
小灰灰要减肥1 小时前
装饰者模式
java
张铁铁是个小胖子1 小时前
MyBatis学习
java·学习·mybatis