Принцип подстановки Лисков Инвариантность

Может ли кто-нибудь объяснить мне, почему этот код не работает? Как это связано с правилом LSP? Что означает инвариантность в этом контексте?

Пример взят из книги Adaptive code via 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‹LSP.core.B, LSP.core.B› в System.Collections.Generic.IDictionary‹LSP.core.A, LSP.core.A› '. Существует явное преобразование (вы пропустили приведение?)
Ошибка CS0266 Не удается неявно преобразовать тип «System.Collections.Generic.Dictionary‹LSP.core.A, LSP.core.A›» в «System.Collections. Generic.IDictionary‹LSP.core.B, LSP.core.B›». Существует явное преобразование (вам не хватает приведения?)


person Adouani Riadh    schedule 14.04.2021    source источник
comment
Можете ли вы уточнить, почему ваш код не работает? Чего вы ожидали и что произошло на самом деле? Если вы получили исключение/ошибку, опубликуйте строку, в которой она произошла, и сведения об исключении/ошибке, которые можно сделать с помощью минимально воспроизводимого пример. Пожалуйста, отредактируйте свой вопрос, чтобы добавить в него эти данные, иначе мы не сможем помочь.   -  person gunr2171    schedule 14.04.2021
comment
@gunr2171 gunr2171 Я отредактировал вопрос, добавив ошибку   -  person Adouani Riadh    schedule 14.04.2021
comment
@MindSwipe обе инструкции не работают   -  person Adouani Riadh    schedule 14.04.2021


Ответы (2)


Простой пример:

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
person Johnathan Barclay    schedule 14.04.2021

Это связано с тем, что IDictionary является классом generic.

Возьмем следующий пример:

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

B является подклассом A, поэтому это разрешено.

A varName = new B();

но когда вы используете varName в коде, вы можете получить доступ только к свойствам класса A

Но List<B> не является подклассом List<A>, несмотря на то, что B является подклассом A.
Компилятор не проверяет общий тип параметра на предмет наследования и считает их разными типами.

следующее не допускается

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

какой разрешенный идентификатор использовать правило LSP для заполнения списка

List<A> listName = new List<A>();
ListName.Add(new B());

Но когда вы обращаетесь к ListName[0], компилятор рассматривает объект, который вы получите, как объект типа A.

person Connor Stoop    schedule 14.04.2021
comment
Это действительно просто говорит, что вы не можете этого сделать, а не объясняет, почему. - person Johnathan Barclay; 14.04.2021
comment
@JohnathanBarclay, вы правы, ваше объяснение лучше, если использовать тип C, что делает его намного более понятным - person Connor Stoop; 14.04.2021
comment
Это не КОМПИЛЯЦИЯ, потому что аргументы типа (которые отличаются от параметров типа) определяют универсальный тип. В контексте примера кода выполните следующее: Console.WriteLine(typeof(Dictionary‹A, A›).FullName); Console.WriteLine(typeof(Dictionary‹B, B›).ПолноеИмя); и посмотрите, что он говорит. Короче говоря, Словарь ‹B,B› не является подклассом Словаря ‹A,A›. - person Velja Radenkovic; 15.04.2021
comment
Продолжение комментария выше: LSV говорит: принцип определяет, что объекты суперкласса должны быть заменены объектами его подклассов без нарушения приложения, но сломанный пример пытается заменить -object общего класса (интерфейса) типом аргументы суперкласса с -object универсального класса с аргументами типа подкласса. Он также пытается сделать это в обратном направлении. Ничего из этого не предлагает LSV. - person Velja Radenkovic; 15.04.2021