The_Unknown_Member Ответов: 3

[C#] явное приведение к ссылке tpyes


Давайте возьмем этот код в качестве примера:
public class Shape
{
    // A few example members
    public int X { get; private set; }
    public int Y { get; private set; }
    public int Height { get; set; }
    public int Width { get; set; }
   
    // Virtual method
    public virtual void Draw()
    {
        Console.WriteLine("Performing base class drawing tasks");
    }
}

class Circle : Shape
{
    public override void Draw()
    {
        // Code to draw a circle...
        Console.WriteLine("Drawing a circle");
        base.Draw();
    }
}
class Rectangle : Shape
{
    public override void Draw()
    {
        // Code to draw a rectangle...
        Console.WriteLine("Drawing a rectangle");
        base.Draw();
    }
}
class Triangle : Shape
{
    public override void Draw()
    {
        // Code to draw a triangle...
        Console.WriteLine("Drawing a triangle");
        base.Draw();
    }
}



Поэтому когда я пишу этот код:
class Program
{
    static void Main()
    {
        Circle c = new Circle();
        Shape s = (Shape)c;
    }
}


Как это работает ? Ссылка Shape s теперь указывает на c, рассматриваемый как Тип формы в памяти или ? Если я хочу использовать c для формирования формы, почему я не могу просто написать такой однострочный код
class Program
{
    static void Main()
    {
        Circle c = new Circle();
        (Shape)c;
    }
}


Что я уже пробовал:

Смотрел видеоуроки и читал Microsoft Docs, но все равно очень запутался.

3 Ответов

Рейтинг:
18

F-ES Sitecore

Прежде всего, вы должны понимать ссылочные типы в целом. У вас есть объект Circle где-то в памяти, и у вас есть две переменные, указывающие на него или ссылающиеся на него; одна - "c", а другая - "s". Если вы обновите переменную через "c", то" s " увидит то же самое, что есть только один фактический объект, на который смотрят оба.

Circle c = new Circle();
Shape s = (Shape)c;

c.Height = 123;

int h = s.Height; // h is 123

s.Height = 456;

h = c.Height; // h is 456


Представьте, что ваш объект - это набор русских кукол с куклами внутри друг друга. Итак, у вас есть кукла под названием Circle, внутри нее кукла под названием Shape, а внутри кукла под названием Object. Это ваше дерево наследования (в конечном итоге все наследуется от Object). Есть только один объект вложенных кукол, но «c» удерживает внешнюю куклу Circle, а «s» удерживает куклу Shape внутри нее. Итак, «c» имеет доступ к кукле Circle и всему, что внутри нее, поэтому он может видеть свойства Circle, Shape и Object. Однако, поскольку «s» удерживает внутреннюю куклу Shape, она может видеть только свойства Shape и куклы Object внутри нее. Однако, если «s» обновляет какое-то свойство этой куклы Shape, то «c» также видит это.


Richard MacCutchan

Отличная аналогия.

Maciej Los

5ед!

The_Unknown_Member

Отлично. Всего в 7 предложениях вы очень хорошо объяснили, что такое наследование и полиморфизм на примере реального мира. Большое вам спасибо! Кстати, еще эти русские куклы называются "матрешками", если вы не знали.

[no name]

Одно из лучших объяснений, которое я прочитал до сих пор на этой неделе +5

Рейтинг:
12

OriginalGriff

Вы можете просто ввести это:

Circle c = new Circle();
(Shape)c;
И это действительно C# - он просто не делает ничего полезного, потому что вы отбрасываете значение, которое вы бросили.

Когда вы это сделаете:
Circle c = new Circle();
Shape s = c;
Вы создаете две переменные c и s, и вы заставляете их обоих ссылаться на один и тот же объект. Это как автомобили: "эта машина "и" моя машина " могут по-разному относиться к одному и тому же автомобилю.

При создании класса, производного от базового класса, как это делается с Circle и Shape, переменная Shape может содержать ссылку на любой экземпляр объекта Shape, включая все объекты, которые относятся к классу, производному от формы потому что действие получения класса говорит: "этот новый класс является базовым классом, но он может сделать и больше".
В результате вы можете использовать свойства, методы и события из базового класса в любом экземпляре производного класса. Возвращаясь на мгновение к автомобилям. Если существует базовый класс автомобилей, то вы получаете производителя от него: Mercedes происходит от автомобиля, как и Ford, Ferrari и Audi. Это означает, что каждый автомобиль, производимый Mercedes, является автомобилем, но не означает, что каждый Ford - это Ferrari, потому что у них общая база. Переменная, содержащая автомобиль (например, «эта машина» или «эта машина», «моя машина» или «ваша машина», может делать все, что является общим для всех автомобилей): CountTheWheels (всегда возвращает 4), GetTheColour (потому что все автомобили имеют цвет), Drive (потому что все автомобили управляются очень похожим образом). Но вы не можете взять переменную Mercedes и выполнить для нее метод Ford: ServiceMyFord не существует для моей машины, потому что не было мистера Форда. Я не успел, мистер Бенц сделал это, а требования к обслуживанию другие.

Система использует фактический экземпляр, чтобы определить, какой он на самом деле тип, и вызвать правильный метод.

Например, у Car может быть метод DriveTo по умолчанию, который Mercedes переопределяет, но который Ford переопределяет по-другому - когда вы вызываете DriveTo для переменной Car, система просматривает содержимое, находит, что это Ford, и автоматически вызывает версию метода Ford.

Есть ли в этом смысл?


The_Unknown_Member

Да, это имеет смысл. Вы дали мне реальный пример и очень благодарны за это. Но я спрашивал не об этом. Я ужасно умею объяснять людям разные вещи и ненавижу себя за это. Просто посмотрите на этот код и я думаю вы поймете о чем я спрашиваю:

использование системы;
использование системы.Коллекции.Общий;

программа занятий
{
статический недействительным Главная()
{
List & lt;Person> people = новый список & lt;Person>();
люди.Добавить (новый сотрудник());
люди.Добавить (новый студент());

Человек п = нового сотрудника();
Employee e = p as Employee; / / я хочу спросить, что происходит в этом коде строки, когда я выполняю приведение в части назначения
}
}

класс person
{
public string Name { get; set; }
public int Age { get; set; }
}

классный работник: человек
{
public string ID { get; set; }

общественного недействительными IntroduceSelf()
{
Приставка.Метода WriteLine("Привет, меня зовут {0} и мой паспорт {1}", название, ID);
}
}

ученик класса: человек
{
public int GradeLevel { get; set; }
}

OriginalGriff

Это немного сложнее, чем ваш первоначальный пример: поскольку Employee происходит от Person, не каждый экземпляр Person является Employee - не больше, чем каждое транспортное средство является автомобилем; некоторые из них являются автобусом, грузовиком или мотоциклом.
Поэтому, когда вы используете "как":

Сотрудник e = p как сотрудник;

Значение в e может быть одним из двух разных:
1) это может быть экземпляр работника, если должность занимал сотрудник.
Или
2) это может быть нуль, если П был нулем, или P содержится некий человек, который не был сотрудником:

Студент = новый студент();
Человек p = s;
Сотрудник e = p как сотрудник;

В этом случае задействованный экземпляр является студентом - что означает, что это человек, - поэтому его можно хранить в переменной Person.
Но студент не является сотрудником, у них просто есть "общий предок" - человек, поэтому оператор "as" возвращает null, потому что студент не может быть преобразован в сотрудника.

Подумайте об этом: это единственное разумное решение. Если вы сохранили студента в переменной Employee, вы можете попытаться вызвать "PayWages" для студента - а студент не определяет метод PayWages, потому что никто не хочет ничего платить студентам! :смеяться:
Возвращаясь к автомобильным терминам, и Ford, и Ferrari являются примерами автомобиля - но вы не можете превратить Ford Ka в Ferrari, просто поменяв значки! (И люди будут много смеяться над вами, если вы это сделаете.)

The_Unknown_Member

Я думаю, что понял это.:

Человек п = нового сотрудника();
Сотрудник e = p как сотрудник;

Итак, в первой строке я делаю ссылку на человека с именем "p" и делаю эту ссылку, указывающую на экземпляр объекта Employee. Но поскольку ссылочная переменная имеет тип Person, я могу видеть только конкретные вещи для класса Person. Простыми словами: Я смотрю глазами человека. И во второй строке я делаю ссылочную переменную сотрудника с именем " e "и делаю эту переменную" e", указывающую на тот же адрес, на который указывает" p", но рассматриваемую как сотрудник.

Правильно ли я это понял ?

OriginalGriff

В значительной степени, да!
Единственная разница заключается в том, что даже если ваша переменная p является человеком, ссылающимся на сотрудника, система будет использовать методы сотрудника, если они переопределяют методы человека. Другими словами, если ваш класс Person имеет метод DoIt:

public virtual void DoIt () { консоль.WriteLine ("DoIt Person!"); }

И ваш класс сотрудников переопределяет это:

public override void DoIt () { консоль.WriteLine ("DoIt Employee"); }

Тогда этот код:

Человек п = нового сотрудника();
p. DoIt();

Все равно позвонит сотрудник версии и распечатает:

Сотрудник Доить

Поскольку система знает, что экземпляр является производным классом и имеет более релевантный метод, она использует его.

The_Unknown_Member

Да, я это знаю. Также его вызывает конструктор Employee.

OriginalGriff

Вызов конструктора происходит по разным причинам - это потому, что вы явно создаете объект Employee с ключевым словом "new"

Рейтинг:
1

CPallini

Цитата:
Окружности c = новый круг();
Форма s = (форма)c;
Вам не нужно бросать (потому что Circle является один Shape), пробовать:
Circle c = new Circle();
Shape s = c;



Цитата:
(Форма) c;
Это излишне, потому что, опять же, Circle является один Shape.


Цитата:
Смотрел видеоуроки и читал Microsoft Docs, но все равно очень запутался.
Попробуйте почитать книгу об ООП.


Maciej Los

5ед!

CPallini

Спасибо!

The_Unknown_Member

Ноооо. Я хотел спросить, как работает актерский состав в этом примере:
программа занятий
{
статический недействительным Главная()
{
S форма = круг новый();
Окружности c = (круг)с;
гр.Ничья();
}
}

Я знаю, что такое полиморфизм и как он работает, но я не могу понять, как работает актерский состав в этом примере. Также извините за плохое объяснение, надеюсь, вы поняли, что я имею в виду.