• c# 逆变 / 协变


    个人理解:

    1. 逆变in向上兼容类

    2. 协变out向下兼容类

    在面向对象编程中,尤其是使用泛型时,inout关键字用于限制类型参数的协变性和逆变性。

    1. in关键字(逆变):

      • in关键字用于标记泛型类型参数的逆变性。逆变表示可以使用指定类型的基类或超类作为方法的参数类型。
      • 当一个泛型类或接口使用in关键字限定类型参数时,该泛型类型可以被赋值给比它更通用的类型。
      • 例如,如果有一个只写存储库,你无法通过它查询信息,只能往里面添加动物。这种情况下,可以将泛型类型参数声明为in Animal,表示该类型参数可以是Animal或其任何基类。
    2. out关键字(协变):

      • out关键字用于标记泛型类型参数的协变性。协变表示可以使用指定类型的子类或派生类作为方法的返回类型。
      • 当一个泛型类或接口使用out关键字限定类型参数时,该泛型类型可以被赋值给比它更具体的类型。
      • 例如,如果有一个只读存储库,你无法通过它添加动物,只能查询信息。这种情况下,可以将泛型类型参数声明为out Animal,表示该类型参数可以是Animal或其任何派生类。

    通过使用inout关键字,我们可以在泛型类型参数中限制类型的协变性和逆变性,以确保类型安全性。这样做的好处是能够更容易地对泛型类型进行复用和灵活性。

    代码示例:

    1. // 声明一个只读存储库接口,用于查询动物信息
    2. interface ReadOnlyRepository<out T> {
    3. fun getAll(): List
    4. fun getById(id: String): T?
    5. }
    6. // 声明一个只写存储库接口,用于添加动物
    7. interface WriteOnlyRepository<in T> {
    8. fun add(item: T)
    9. }
    10. // Animal类作为基类
    11. open class Animal(val name: String)
    12. // Dog类继承自Animal
    13. class Dog(name: String) : Animal(name)
    14. // Cat类继承自Animal
    15. class Cat(name: String) : Animal(name)
    16. // 只读存储库实现
    17. class ReadOnlyAnimalRepository : ReadOnlyRepository {
    18. private val animals = listOf(Animal("Lion"), Dog("Buddy"), Cat("Whiskers"))
    19. override fun getAll(): List {
    20. return animals
    21. }
    22. override fun getById(id: String): Animal? {
    23. return animals.find { it.name == id }
    24. }
    25. }
    26. // 只写存储库实现
    27. class WriteOnlyAnimalRepository : WriteOnlyRepository<Animal> {
    28. private val animals = mutableListOf()
    29. override fun add(item: Animal) {
    30. animals.add(item)
    31. }
    32. }
    33. fun main() {
    34. val readOnlyRepo: ReadOnlyRepository = ReadOnlyAnimalRepository()
    35. val writeOnlyRepo: WriteOnlyRepository = WriteOnlyAnimalRepository()
    36. val allAnimals = readOnlyRepo.getAll()
    37. println("All Animals:")
    38. allAnimals.forEach { animal ->
    39. println("- ${animal.name}")
    40. }
    41. val dog1 = Dog("Max")
    42. writeOnlyRepo.add(dog1)
    43. println("\nAdded Dog:")
    44. val addedDog = readOnlyRepo.getById(dog1.name)
    45. println("- ${addedDog?.name}")
    46. val cat1 = Cat("Misty")
    47. // writeOnlyRepo.add(cat1) // Compilation Error: Type mismatch. Required: Dog, Found: Cat
    48. }

    在上面的代码示例中,我们定义了两个存储库接口:ReadOnlyRepositoryWriteOnlyRepositoryReadOnlyRepository接口声明了返回类型out T,表示它只能查询(读取)动物信息。WriteOnlyRepository接口声明了参数类型in T,表示它只能添加(写入)动物。

    然后,我们创建了一个ReadOnlyAnimalRepository类来实现ReadOnlyRepository接口,用于查询动物信息。同样地,我们还创建了一个WriteOnlyAnimalRepository类来实现WriteOnlyRepository接口,用于添加动物。

    main函数中,我们首先将ReadOnlyAnimalRepository赋值给readOnlyRepo变量,这是合法的,因为ReadOnlyAnimalRepository实现了ReadOnlyRepository接口。

    然后,我们将WriteOnlyAnimalRepository赋值给writeOnlyRepo变量,这也是合法的,因为WriteOnlyAnimalRepository实现了WriteOnlyRepository接口。注意,虽然我们将其赋值给writeOnlyRepo变量,但我们仍然只能使用add方法添加

  • 相关阅读:
    Spring之AOP入门篇
    CentOS7 升级OpenSSH9.0全过程和坑
    Spring Boot 注解
    velocity 调用hutool 实现首字母大写
    15篇MyBatis-Plus系列集合篇「值得收藏学习」
    【go-zero】go如何1秒优雅地生成 protobuf 中的CRUD (关于 go-zero RPC中如何快速生成protobuf)
    数字孪生技术:新零售的未来之路
    IPython Jupyter NumPy Pandas Matplotlib Scikit-Learn statsmodels
    【计算机网络】 传输层
    FNN、DeepFM与NFM
  • 原文地址:https://blog.csdn.net/dongnihao/article/details/134555134