Liskov 替换原则 不变性

问题描述

谁能向我解释为什么这段代码不起作用?与LSP规则有什么关系?在这种情况下,不变性是什么意思?

示例来自 C# 书籍中的自适应代码

class Program
{
    static void Main(string[] args)
    {
        IDictionary<A,A> dict1 = new Dictionary<B,B>();
        IDictionary<B,B> dict2 = new Dictionary<A,A>();    
    }
}
public class A { }
public class B: A { }

错误信息

错误 CS0266 无法将类型“System.Collections.Generic.Dictionary”隐式转换为“System.Collections.Generic.IDictionary'。存在显式转换(您是否缺少演员表?)
错误 CS0266 无法将类型“System.Collections.Generic.Dictionary”隐式转换为“System.Collections.Generic.IDictionary” '。存在显式转换(您是否缺少强制转换?)

解决方法

一个简单的例子:

class A { }
class B : A { }
class C : A { }

// Imagine this was allowed:
var dict1 = new Dictionary<B,B>();
IDictionary<A,A> dict2 = dict1;

// Then you did this:
var c = new C();
dict2[c] = c;

// And finally tried to do this:
B b = dict1.Keys.First(); // Breaks type safety,because First() returns a C instance
,

这是因为 IDictionary 是一个 f x = Suc x 类。

举个例子:

generic

B 是 A 的子类,所以这是允许的。

public class A { }
public class B: A { }

但是当您在代码中使用 A varName = new B(); 时,您只能访问 A 类的属性

但是 varName 不是 List<B> 的子类,即使 List<A>B 的子类。
编译器不会检查继承的泛型参数类型,而是将它们视为不同的类型。

以下是不允许的

A

允许id使用LSP规则填充列表

List<A> listName = new List<B>();

但是当您访问 List<A> listName = new List<A>(); ListName.Add(new B()); 时,编译器会将您获得的对象视为类型 ListName[0] 对象。