> 技术文档 > unity游戏开发-7-unity 基础:继承、接口、属性、索引器、事件、访问控制_unity 类的属性怎么写

unity游戏开发-7-unity 基础:继承、接口、属性、索引器、事件、访问控制_unity 类的属性怎么写


目录

  • 前言
  • 1. 继承
  • 2. 接口
    • 2.1 用途 & 优势
    • 2.2 可以定义的内容
    • 2.3 特点
    • 2.2 定义 & 继承
    • 2.3 使用 & 多态
  • 3. 属性
    • 3.1 用途
    • 3.2 语法
      • 3.2.1 简写
      • 3.2.2 完整写法:定义私有成员、实现 get|set
      • 3.2.3 只读 & 只写 属性
  • 4. 索引器
    • 4.1 用途
    • 4.2 语法
  • 5. 事件
    • 5.1 用途
    • 5.2 语法
      • 5.2.1 定义
      • 5.2.2 使用
  • 6. 访问控制
    • 6.1 访问修饰符及其作用范围
    • 6.2 访问控制的具体应用
    • 6.3 访问控制的具体示例

前言

上一篇文章介绍了 c# 中基础的:数据类型、条件、循环、函数、委托、泛型 相关的内容。对于自定义的数据类型:类、结构,其非常重要的特性—— 派生 没有详细说明。
本篇文章将介绍 c# 中和 派生 相关的内容,包括:继承|派生、接口、属性、访问控制。

1. 继承

  • 父类 & 子类

    • 父类也称基类,子类也称派生类
    • 父类派生出子类,或者说 子类继承自父类
    • 比如一个简单的父类:动物,子类1:猫,子类2:狗
  • 继承的用途

    • 描述类的逻辑关系:在 面向对象 的编程语言中,一般都会提供 类、继承 相关的语法,用以描述 某一系列 抽象集合 的 is-a 关系。
    • 代码复用:在派生类中可以仅重写于父类有差异的函数,达到对无差异的函数的复用
    • 多态:基类引用子类的实例时,调用该引用的函数,实际调用的是实际子类对象的函数,也就是用基类类型可以实现对不同子类函数的访问。
  • 代码样例(类继承)

    • 基类

      public class Animal{ public string Name { get; set; } public Animal(string name) { Name = name; } public virtual void MakeSound() { Console.WriteLine(\"This animal makes a sound.\"); }}
    • 派生类

      public class Dog : Animal{ public Dog(string name) : base(name) { } public override void MakeSound() { Console.WriteLine(\"Woof!\"); }}
    • 调用(多态)

      public class Program{ public static void Main() { Animal myAnimal = new Animal(\"Generic Animal\"); myAnimal.MakeSound(); // 输出:This animal makes a sound. Dog myDog = new Dog(\"Buddy\"); myDog.MakeSound(); // 输出:Woof! // 多态:通过基类引用调用派生类的方法 Animal myAnimalDog = new Dog(\"Max\"); myAnimalDog.MakeSound(); // 输出:Woof! }}
  • c# 中的 “基类”

    • 除了 class,在 c# 中还提供了 接口(Interface)用于定义接口,可以被继承。(在第2节详细介绍)

2. 接口

2.1 用途 & 优势

  • 定义契约

    • 接口定义了一组方法、属性、索引器或事件的签名,但不提供具体实现。实现接口的类或结构体必须提供这些成员的具体实现。
    • 这种方式定义了类或结构体必须遵守的契约,确保它们具有一致的行为。
  • 实现多态

    • 多个类可以实现同一个接口,从而实现多态。通过接口引用,可以调用接口中定义的方法,而不关心具体实现。
    • 这使得代码更加灵活,可以编写通用的代码来处理不同类型的对象。
  • 解耦
    接口可以减少类之间的耦合度,使得代码更加灵活和可维护。实现接口的类只需要关心接口定义的成员,而不需要知道其他类的具体实现。

  • 代码复用
    接口允许不同类共享相同的方法签名,从而提高代码的复用性。

  • 扩展性
    接口可以很容易地扩展。如果需要添加新的方法或属性,可以在接口中添加新的签名,而不需要修改现有的实现。

2.2 可以定义的内容

  • 方法(Methods):
    接口可以定义方法的签名,不提供具体实现(c# 8.0 后可以定义默认实现)。
    实现接口的类或结构体必须提供这些方法的具体实现。

  • 属性(Properties):
    接口可以定义属性的签名,但不提供具体实现。
    实现接口的类或结构体必须提供这些属性的具体实现。

  • 索引器(Indexers):
    接口可以定义索引器的签名,但不提供具体实现。
    实现接口的类或结构体必须提供这些索引器的具体实现。

  • 事件(Events):
    接口可以定义事件的签名,但不提供具体实现。
    实现接口的类或结构体必须提供这些事件的具体实现。

2.3 特点

  • 接口本身不能实例化,只能通过实现接口的类或结构体来实例化。
  • 接口中的成员默认是 public,不能有访问修饰符
  • 接口可以继承接口,支持多继承。
  • 一个类可以实现多个接口,从而实现多继承的效果。
  • C# 8.0 开始,接口中的函数可以提供默认实现,实现接口的类可以选择性地覆盖这些方法。

2.2 定义 & 继承

  • 关键字

    • interface
    • 类似于 类的关键字 class
  • 代码样例

    • 接口定义

      public interface IAnimal{ void MakeSound();}public interface INameable{ string Name { get; set; }}public interface IPet : IAnimal, INameable{ void Play();}

      注:上面这个例子的特点有:
      ① 接口多继承:IPet 接口继承了多个接口
      ② IAnimal 接口中不实现具体的方法
      ③ INameable 接口中定义了属性

    • 继承:接口实现

      public class Dog : IPet{ public string Name { get; set; } public void MakeSound() { Console.WriteLine(\"Woof!\"); } public void Play() { Console.WriteLine($\"{Name} is playing.\"); }}public class Cat : IPet{ public string Name { get; set; } public void MakeSound() { Console.WriteLine(\"Meow!\"); } public void Play() { Console.WriteLine($\"{Name} is playing.\"); }}

2.3 使用 & 多态

  • 代码样例
public class Program{ public static void Main() { IPet dog = new Dog { Name = \"Buddy\" }; IPet cat = new Cat { Name = \"Whiskers\" }; dog.MakeSound(); // 输出:Woof! dog.Play(); // 输出:Buddy is playing. cat.MakeSound(); // 输出:Meow! cat.Play(); // 输出:Whiskers is playing. }}

3. 属性

3.1 用途

在 C# 中,属性(Property) 是一种特殊的成员,它允许你访问类或结构体中的私有字段,同时提供了一种更安全和灵活的方式来控制对字段的访问。属性通常用于封装字段,隐藏字段的实现细节,并提供额外的逻辑(如验证输入)。

3.2 语法

3.2.1 简写

  • 代码样例
public class MyClass{ // 定义一个自动实现的属性 public int Property { get; set; }}public class Program{ public static void Main() { MyClass obj = new MyClass(); // 设置属性的值 obj.Property = 10; // 获取属性的值 Console.WriteLine(obj.Property); // 输出:10 }}
  • 注:虽然不需要显式定义私有字段,但 编译器会自动生成 一个私有字段来存储属性的值。

3.2.2 完整写法:定义私有成员、实现 get|set

  • 代码示例
public class MyClass{ private int _property; // 私有字段 // 定义一个属性 public int Property { get { return _property; } // 返回私有字段的值 set { if (value < 0) { throw new ArgumentException(\"Value cannot be negative.\"); } _property = value; // 设置私有字段的值 } }}public class Program{ public static void Main() { MyClass obj = new MyClass(); // 设置属性的值 obj.Property = 10; // 获取属性的值 Console.WriteLine(obj.Property); // 输出:10 // 尝试设置一个负值 try { obj.Property = -5; } catch (ArgumentException ex) { Console.WriteLine(ex.Message); // 输出:Value cannot be negative. } }}

3.2.3 只读 & 只写 属性

  • 只读:仅定义 get

    • 代码样例

      public class MyClass{ private int _field; // 私有字段 // 只读属性 public int ReadOnlyProperty { get { return _field; } } // 公共方法用于设置只读属性的值 public void SetProperty(int value) { _field = value; // 通过私有字段设置值 }}
  • 只写:仅定义 set

    • 代码样例
    public class MyClass{ private int _field; // 私有字段 // 只写属性 public int WriteOnlyProperty { set { _field = value; } } // 公共方法用于获取只写属性的值 public int GetProperty() { return _field; // 通过私有字段获取值 }}

4. 索引器

4.1 用途

  • 在 C# 中,索引器(Indexer) 是一种特殊的成员,它允许对象以类似数组的方式被索引。索引器可以提供基于整数、字符串或其他类型的索引访问,使得对象的使用更加灵活和直观。
  • 索引器通常用于以下场景:
    • 提供基于索引的访问:允许对象像数组或字典一样被访问。
    • 封装内部数据结构:隐藏内部数据的实现细节,只暴露索引访问接口。
    • 提供自定义的索引逻辑:可以基于不同的键类型(如字符串、整数等)提供自定义的索引访问。

4.2 语法

  • 整型索引

    • 代码样例

      • 定义
      public class MyCollection{ private string[] _items = new string[10]; public string this[int index] { get { if (index < 0 || index >= _items.Length) { throw new IndexOutOfRangeException(\"Index out of range.\"); } return _items[index]; } set { if (index < 0 || index >= _items.Length) { throw new IndexOutOfRangeException(\"Index out of range.\"); } _items[index] = value; } }}
      • 使用
      public class Program{ public static void Main() { MyCollection collection = new MyCollection(); // 设置值 collection[0] = \"Item 1\"; collection[1] = \"Item 2\"; // 获取值 Console.WriteLine(collection[0]); // 输出:Item 1 Console.WriteLine(collection[1]); // 输出:Item 2 }}
  • 字符串索引

    • 代码样例
      • 定义
      public class MyDictionary{ private Dictionary<string, string> _data = new Dictionary<string, string>(); public string this[string key] { get { if (_data.ContainsKey(key)) { return _data[key]; } throw new KeyNotFoundException(\"Key not found.\"); } set { _data[key] = value; } }}
      • 使用
      public class Program{ public static void Main() { MyDictionary dictionary = new MyDictionary(); // 设置值 dictionary[\"key1\"] = \"Value 1\"; dictionary[\"key2\"] = \"Value 2\"; // 获取值 Console.WriteLine(dictionary[\"key1\"]); // 输出:Value 1 Console.WriteLine(dictionary[\"key2\"]); // 输出:Value 2 }}

5. 事件

5.1 用途

  • 在 C# 中,事件(Event) 是一种特殊的成员,用于实现发布-订阅模式。事件允许类或对象在特定情况下通知其他对象,从而实现对象之间的解耦和交互。事件通常用于处理用户界面事件(如按钮点击、窗口关闭等),但也可以用于任何需要通知的场景。

  • 事件的基本概念

    • 发布者(Publisher):
      定义事件的类或对象,负责触发事件。
      通常包含一个或多个事件成员,以及触发事件的方法。

    • 订阅者(Subscriber):
      注册事件的类或对象,负责处理事件。
      通过事件处理器(Event Handler)接收事件通知。

    • 事件处理器(Event Handler):
      一个方法,用于处理事件。
      事件处理器的签名必须与事件的委托类型匹配。

5.2 语法

5.2.1 定义

  • 事件(发布者)
    • 事件的定义通常包括以下步骤:
      定义一个委托(Delegate),用于指定事件处理器的签名。
      定义一个事件成员,使用 event 关键字。
      提供一个方法来触发事件。

    • 代码示例

using System;public class EventPublisher{ // 定义委托 public delegate void MyEventHandler(object sender, EventArgs e); // 定义事件 public event MyEventHandler MyEvent; // 触发事件的方法 public void DoSomething() { Console.WriteLine(\"Doing something...\"); // 触发事件 OnMyEvent(); } // 触发事件的辅助方法 protected virtual void OnMyEvent() { MyEvent?.Invoke(this, EventArgs.Empty); }}
  • 订阅者
    • 订阅者需要注册事件,并提供事件处理器来处理事件。
    • 代码示例
using System;public class EventSubscriber{ public void Subscribe(EventPublisher publisher) { // 注册事件 publisher.MyEvent += HandleEvent; } public void Unsubscribe(EventPublisher publisher) { // 取消注册事件 publisher.MyEvent -= HandleEvent; } // 事件处理器 public void HandleEvent(object sender, EventArgs e) { Console.WriteLine(\"Event handled by EventSubscriber.\"); }}

5.2.2 使用

  • 代码示例
using System;public class Program{ public static void Main() { EventPublisher publisher = new EventPublisher(); EventSubscriber subscriber = new EventSubscriber(); // 注册事件 subscriber.Subscribe(publisher); // 触发事件 publisher.DoSomething(); // 输出:Doing something... Event handled by EventSubscriber. // 取消注册事件 subscriber.Unsubscribe(publisher); // 再次触发事件(不会输出) publisher.DoSomething(); // 输出:Doing something... }}

6. 访问控制

在 C# 中,访问控制用于限制代码的可见性和访问权限。C# 提供了五种访问修饰符,分别是 publicprivateprotectedinternalprotected internal。每种访问修饰符都有其特定的用途和作用范围。

6.1 访问修饰符及其作用范围

  • public

    • 作用范围:任何地方都可以访问。
    • 用途:用于公开类、方法、属性等,使其可以被外部代码访问。
  • private

    • 作用范围:仅在定义它的类或结构体内部可以访问。
    • 用途:用于隐藏类或结构体的内部实现细节,防止外部代码直接访问。
  • protected

    • 作用范围:仅在定义它的类或结构体及其派生类中可以访问。
    • 用途:用于允许派生类访问基类的成员,但不允许外部代码访问。
  • internal

    • 作用范围:仅在同一个程序集(Assembly)内可以访问。
    • 用途:用于限制类或成员的访问范围,使其仅在当前项目内可见。
  • protected internal

    • 作用范围:在同一个程序集内或派生类中可以访问。
    • 用途:结合了 protectedinternal 的特性,允许在当前程序集内或派生类中访问。

6.2 访问控制的具体应用

  • 类(Class)
    类的访问修饰符决定了类的可见性。
public class PublicClass{ // 公共类,任何地方都可以访问}private class PrivateClass{ // 私有类,仅在定义它的类或结构体内部可以访问}internal class InternalClass{ // 内部类,仅在同一个程序集内可以访问}
  • 成员(Members)
    成员(如字段、方法、属性等)的访问修饰符决定了它们的可见性。
public class MyClass{ public int PublicField; // 公共字段,任何地方都可以访问 private int PrivateField; // 私有字段,仅在类内部可以访问 protected int ProtectedField; // 受保护字段,仅在类及其派生类中可以访问 internal int InternalField; // 内部字段,仅在同一个程序集内可以访问 protected internal int ProtectedInternalField; // 受保护内部字段,仅在同一个程序集内或派生类中可以访问}
  • 方法(Methods)
    方法的访问修饰符决定了它们的可见性。
public class MyClass{ public void PublicMethod() { // 公共方法,任何地方都可以访问 } private void PrivateMethod() { // 私有方法,仅在类内部可以访问 } protected void ProtectedMethod() { // 受保护方法,仅在类及其派生类中可以访问 } internal void InternalMethod() { // 内部方法,仅在同一个程序集内可以访问 } protected internal void ProtectedInternalMethod() { // 受保护内部方法,仅在同一个程序集内或派生类中可以访问 }}
  • 属性(Properties)
    属性的访问修饰符决定了它们的可见性。属性的 getset 访问器也可以有不同的访问修饰符。
public class MyClass{ public int PublicProperty { get; set; } // 公共属性,任何地方都可以访问 private int PrivateProperty { get; set; } // 私有属性,仅在类内部可以访问 protected int ProtectedProperty { get; set; } // 受保护属性,仅在类及其派生类中可以访问 internal int InternalProperty { get; set; } // 内部属性,仅在同一个程序集内可以访问 protected internal int ProtectedInternalProperty { get; set; } // 受保护内部属性,仅在同一个程序集内或派生类中可以访问 // 属性的访问器可以有不同的访问修饰符 public int ReadOnlyProperty { get; private set; } // 只读属性,外部只能读取,不能设置 public int WriteOnlyProperty { private get; set; } // 只写属性,外部只能设置,不能读取}
  • 接口(Interfaces)
    接口的成员(方法、属性等)默认是 public,不能显式指定访问修饰符。接口本身可以使用 publicinternalprotected internal 修饰符。
public interface IMyInterface{ void Method(); // 默认是 public int Property { get; set; } // 默认是 public}

6.3 访问控制的具体示例

  • 类和成员
public class MyClass{ public int PublicField; // 公共字段 private int PrivateField; // 私有字段 protected int ProtectedField; // 受保护字段 internal int InternalField; // 内部字段 protected internal int ProtectedInternalField; // 受保护内部字段 public void PublicMethod() // 公共方法 { Console.WriteLine(\"Public method\"); } private void PrivateMethod() // 私有方法 { Console.WriteLine(\"Private method\"); } protected void ProtectedMethod() // 受保护方法 { Console.WriteLine(\"Protected method\"); } internal void InternalMethod() // 内部方法 { Console.WriteLine(\"Internal method\"); } protected internal void ProtectedInternalMethod() // 受保护内部方法 { Console.WriteLine(\"Protected internal method\"); } public int PublicProperty { get; set; } // 公共属性 private int PrivateProperty { get; set; } // 私有属性 protected int ProtectedProperty { get; set; } // 受保护属性 internal int InternalProperty { get; set; } // 内部属性 protected internal int ProtectedInternalProperty { get; set; } // 受保护内部属性}
  • 派生类
public class DerivedClass : MyClass{ public void Test() { PublicMethod(); // 可以访问 // PrivateMethod(); // 错误:私有方法不可访问 ProtectedMethod(); // 可以访问 // InternalMethod(); // 错误:内部方法不可访问(除非在同一程序集中) ProtectedInternalMethod(); // 可以访问 }}