contravariance

本文通过C++代码示例介绍了反变的概念,即如何将派生类的成员函数指针转换为基类的成员函数指针。展示了不同类型的成员函数指针之间的转换,并解释了可能引发的问题。
反变:把基类里指向成员函数的指针指向派生类中的成员函数。很可能导致错误,就好像把派生类指针指向基类一样。
  1. #include <iostream>
  2. using namespace std;
  3. class Base
  4. {
  5. public:
  6.     void foo(){}
  7. };
  8. class Derived: public Base
  9. {
  10. public:
  11.     void goo(){}
  12. };
  13. typedef void (Base::*MethodOfBase)();
  14. typedef void (Derived::*MethodOfDerived)();
  15. int main()
  16. {
  17.     MethodOfBase methodB;
  18.     MethodOfDerived methodD;
  19.     methodB = static_cast<MethodOfBase>(&Derived::goo);    // contravariance
  20.     methodB = &Base::foo;       // it is OK
  21.     methodD = &Derived::goo;    // it is OK
  22.     methodD = &Base::foo;       // it is OK
  23.     return 0;
  24. }

 
斜变与逆变 1. 基本概念 协变(Covariance): • 允许使用派生类型替换基类型 • 适用于输出位置(返回值、只读场景) • 使用 out 关键字标记 逆变(Contravariance): • 允许使用基类型替换派生类型 • 适用于输入位置(方法参数) • 使用 in 关键字标记 2. 协变示例 自定义协变接口: ```c# public interface IReadOnlyList<out T> { T this[int index] { get; } int Count { get; } } ``` 协变使用: ```c# List<Employee> employees = new List<Employee>(); List<Person> personList = employees.Select(e => (Person)e).ToList(); // 协变:Employee -> Person IReadOnlyList<Person> person = new ReadOnlyList<Person>(personList); ``` 3. 逆变示例 逆变委托: ```c# // 定义接受基类参数的Action Action<Person> printPerson = (Person p) => Console.WriteLine(p.Name); // 逆变:Person -> Employee(基类作为派生类使用) Action<Employee> printEmployee = printPerson; printEmployee(new Employee { Name = "我类个豆", Company = "Google Ai" }); ``` 4. .NET 内置协变/逆变接口 协变接口: ```c# // IEnumerable<out T> - 只读枚举 IEnumerable<Employee> employees = new List<Employee>(); IEnumerable<Person> persons = employees; // 协变 // IEnumerator<out T> - 只读枚举器 IEnumerator<Employee> empEnum = employees.GetEnumerator(); IEnumerator<Person> personEnum = empEnum; // 协变 ``` 逆变接口: ```c# // IComparer<in T> - 比较器参数位置逆变 IComparer<Person> personComparer = new PersonComparer(); IComparer<Employee> employeeComparer = personComparer; // 逆变 ``` 委托协变/逆变: ```c# // Func<out TResult> - 返回值协变 Func<Employee> getEmployee = () => new Employee(); Func<Person> getPerson = getEmployee; // 协变 // Action<in T> - 参数逆变 Action<Person> actPerson = p => Console.WriteLine(p.Name); Action<Employee> actEmployee = actPerson; // 逆变 ``` 5. 协变与逆变规则 协变限制: ```c# public interface IValidCovariant<out T> { T GetValue(); // ✅ 正确:返回值位置 T ReadOnlyProperty { get; } // ✅ 正确:只读属性 } public interface IInvalidCovariant<out T> { void SetValue(T value); // ❌ 错误:参数位置不能协变 T ReadWriteProperty { get; set; } // ❌ 错误:写入位置不能协变 } ``` 逆变限制: ```c# public interface IValidContravariant<in T> { void Process(T item); // ✅ 正确:参数位置 } public interface IInvalidContravariant<in T> { T GetValue(); // ❌ 错误:返回值位置不能逆变 } ``` 6. 实际应用场景 集合处理: ```c# // 利用IEnumerable<out T>协变处理不同类型的集合 public static void ProcessPersons(IEnumerable<Person> persons) { foreach (var person in persons) { Console.WriteLine(person.Name); } } List<Employee> employees = new List<Employee>(); ProcessPersons(employees); // 协变:Employee集合作为Person集合使用 ``` 事件处理: ```c# // 事件处理器利用逆变接受更通用的参数类型 public event Action<Person> PersonEvent; Action<Employee> employeeHandler = emp => Console.WriteLine(emp.Name); PersonEvent += employeeHandler; // 逆变:Employee处理器用于Person事件 ``` 7. 注意事项 类型安全:编译器确保协变/逆变的类型安全 只读场景:协变仅适用于只读操作 参数场景:逆变仅适用于参数输入 性能影响:协变/逆变不会影响运行时性能 8. 常见误区 List 不支持协变:List<Employee> 不能直接赋值给 List<Person> 混淆协变与逆变:记住"协变是输出,逆变是输入" 过度使用:只在需要类型灵活性时使用,避免不必要的复杂性
08-06
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值