Loading...
dobri19 avatar dobri19 1 Точки

dynamic polymorphism

 Някой може ли да ми разясни, ако имам абстрактен клас Animal с абстрактни методи, и съответно негов наследник Dog  с имплементация на тези методи, при dynamic polymorphism, каква е разликата и защо трябва да предпочита едната декларация Animal dog = new Dog() или другата Dog dog = new Dog() , като съм наясно че при извикаване на методите в единия случай ще имаме runtime, а при другия compiletime , и съответно първият ще е по-бавен, а резултата е един и същ?

Тагове:
0
C# OOP Basics 16/01/2017 11:33:50
ktrajkov avatar ktrajkov 4 Точки
Animal[] animals =new[] { new Dog(), new Cat()};

Това как би го направил?

0
16/01/2017 11:57:20
dobri19 avatar dobri19 1 Точки

Animal[] animals = new Animal[2];
animals[0] = new Dog();
animals[1] = new Cat();

или

List<Animal> animals = new List<Animal>();
animals.Add(new Dog());
animals.Add(new Cat());

, а обект от тип Animal не мога да добавям защото е абстрактен клас.

0
val4o89 avatar val4o89 240 Точки

На абстрактните класове не можеш да създаваш инстанция, за пример от живия живот - не съществува животното "животно", то може да бъде котка, куче (нещо конкретно).

0
RoYaL avatar RoYaL Trainer 6849 Точки

Dog dog = new Dog(); - това просто не е полиморфизъм. Тук се използва method overloading, което може да бъде постигнато и просто с два метода в един клас, без да има нужда от клас родител.

1
dobri19 avatar dobri19 1 Точки

 Но абстрактния ми метод няма тяло, a на наследника ми метода е override. Така в този случай може ли да говорим за overloading (когато името на метода и сигнатурата при двата са еднакви)?

0
RoYaL avatar RoYaL Trainer 6849 Точки

Ххората обикновено казват статичен полиморфизъм на друг термин, а именно на ситуация в която се получава Overloading (възможно е това да се получи косвено, когато компилаторът вземе методите от базовите класове и ги залепи). Извикването на различен метод в зависимост от сигнатурата наричат наличието на много форми. 

В твоя сценарий, който си дал е абсолютно все едно кой от двата начина ще ползваш - резултатите ще са едни и същи.

0
16/01/2017 12:55:22
dobri19 avatar dobri19 1 Точки

 Мерси, а може ли да дадеш някакъв пример при който печеля нещо от декларирането на обект през бащиния клас? (Animal dog = new Dog()     >    Dog dog = new Dog() )

0
val4o89 avatar val4o89 240 Точки

Един от плюсовете на Animal dog = new Dog() е, че после можеш да декларираш котка (Animal cat = new Cat()), куче, жираф, и т.н. , да ги вкараш в някаква колекция (примерно List<Animal>()) и чрез един форийч (или друг цикъл) да изциклиш всичките животни и на всяко едно да повикаш някой от наследените методи (примерно MakeSound(), Eat(), Walk(), Die().... и т.н.). Като цяло въпроса за абстракцията и полиморфизма е доста широка тема. За по-добро разбиране те съветвам да гледаш лекцията интерфейси и абстракция

0
16/01/2017 12:01:19
dobri19 avatar dobri19 1 Точки

Да благодаря за инфото, но точно това може да се прави и когато са декларирани през своя клас и пак ще влязат в списък с бащиния тип, който може да се обхожда и извикват техните наследени методи.

0
val4o89 avatar val4o89 240 Точки

Да, прав си, можеш, но от гледна точка на енкапсулация няма смисъл да даваш достъп до методи, пропъртита и т.н. от конкретния клас в случай, че в последствие ще ползваш тези на родителя. Зависи от сценария.

0
dobri19 avatar dobri19 1 Точки

 Тоест ако в наследения клас имам допълнителни методи и пропъртита и искам да ги използвам го декларирам  Dog dog = new Dog() ,  а ако не искам достъп до тях като Animal dog = new Dog()?(Въпреки, че щом съм ги създал в класа явно ще искам да ги ползвам).

0
dobri19 avatar dobri19 1 Точки

 Прочетох няколко от статиите за полиморфизъм от Google, и мисля че попаднах на част от отговорите които търсех. Това което аз разбрах е, че и при двата посочени от мене примера имаме полиморфизъм. В случая когато извикваме методи за Dog dog = new Dog(), имаме Compile time Polymorphism or Early Binding - Examples of early binding are overloaded methods, overloaded operators and overridden methods that are called directly by using derived objects. Тоест в този случаи имаме - overridden methods that are called directly by using derived objects. А когато извикваме методи за Animal dog = new Dog(), имаме Runtime Polymorphism or Late Binding - Example of late binding is overridden methods that are called using base class object. А относно предимството на втория начин пише - Advantage of late binding is flexibility. И така отново ми останаха въпросите, какво точно е това flexibility, и кога да ползвам Runtime Polymorphism or Late Binding?  

0
vrabeca avatar vrabeca 26 Точки

http://csharp-video-tutorials.blogspot.bg/2012/07/part-55-c-tutorial-late-binding-using.html в тая статия автора е написал че Late Binding  се използва когато първоначално не знаеш типа на класа и го взимаш с reflection или например декларираш абстрактен клас

Animal animal;

и след това според инпута инициализираш какъв тип е:

animal = new Dog(); или animal = new Cat();

0
RoYaL avatar RoYaL Trainer 6849 Точки

Записът Dog dog = new Dog(); точно така написан без друг контекст няма никаква разлика от това да го напишеш Animal dog = new Dog();, ако методът в Animal е абстрактен. Ако това е целият въпрос - то отговорът е: няма предимство второто пред първото.

Обаче има няколко други ситуации, които може да са интересни.

1. При неправилно реализирана архитектура, методът в базовия клас може да не е Абстрактен или Виртуален, тогава наследнкът го скрива. При следната ситуация

class Animal
{
    public void A()
    {
        Console.WriteLine("aaa");
    }
}

class Dog : Animal
{
    public void A()
    {
        Console.WriteLine("bbb");
    }
}

Имаме следните резултати:

        Animal a = new Dog();
        a.A(); // aaa
        Dog b = new Dog();
        b.A(); // bbb

2. Не се възползваме правилно от абстракцията и естествената капсулация, която тя произвежда. Нека си представим ситуация, в която Animal е реализиран правилно с abstrac/virtual метод, но неговите наследници имат специфични за тях методи (нещо съвсем нормално):

class Animal
{
    public virtual void A()
    {
        Console.WriteLine("aaa");
    }
}

class Dog : Animal
{
    public override void A()
    {
        Console.WriteLine("bbb");
    }

    public void SpecificDogMethod()
    {
        Console.WriteLine("Specific for dog");
    }
}

class Cat : Animal
{
    public override void A()
    {
        Console.WriteLine("ccc");
    }

    public void SpecificCatMethod()
    {
        Console.WriteLine("Specific for cat");
    }
}

При запис Animal animal = new Dog(); и използване на променливата animal ние ще виждаме метода "A()" само. Което е супер, защото можем лесно да подменим new Dog(); с new Cat(); и кодът да работи.

        Animal animal = new Dog();
        animal.A(); // "bbb"
        animal.SpecifiDogMethod(); // compile error - този метод не съществува в Animal
        animal.SpecificCatMethod(); // compile error - този метод не съществува в Animal

Съответно ако имаме реда:

        Animal animal = new Dog();
        animal.A(); // "bbb"

Лесно можем да го заменим с

        Animal animal = new Cat();
        animal.A(); // "ccc"

И просто изходът ще е различен, но кодът ще работи правилно. Добре, обаче ако имаме инициализацията от първия прмер, ние преспокойно можем да извикаме специфичен за кучето метод, нали?:

        Dog animal = new Dog();
        animal.SpecificDogMethod(); // outputs: "Specific for dog"

Така обаче сме крайно вързани за методите на кучето. А вярвай ми, напишеш ли Dog animal = new Dog(); никой по-късно в кода няма да се съобрази да не ползва специфични методи за кучето, просто защото като цъкне точка ще го види в контексното меню и ще хо ползва. Така, че нещо може ли да се обърка, то вероятно ще се обърка. А ние искаме да минимизираме риска т.е. да не позволяваме на хората да объркат (затова и C# не позволява int x = 1; по-късно да му кажеш x = "pesho"; минимизира възможните грешки)

Не можем да подменим променливата animal повече с Cat, защото котката няма да има специфични за кучето методи:

        Cat animal = new Cat(); // променен е този ред
        animal.SpecificDogMethod(); // compile error - котката няма метод "SpecificDogMethod"

3. Може да гледаш със същото око и на аргументи на методи. Там имаме същия проблем. Поискаме ли като аргумент на метод дадена Куче или Котка, вместо Животно, не можем да го заменим.

4. Динамичното bind-ване обикновено реферира към настъпило динамично събитие според което се определя дали ще стане куче или котка. Ако потребителят иска да избере едно от двете, и практически нямаш шанс да му позволиш, ако променливата е от тип Куче.

        string animalType = Console.ReadLine();
        Animal animal = null;
        if (animalType == "Dog")
        {
            animal = new Dog();
        }
        else if (animalType == "Cat")
        {
            animal = new Cat();
        }
        // после извикваме някакъв метод от Животно
        // после друг, но никога специфичен за Котка/Куче
        // после например подаваме променливата на друг метод да прави нещо с нея

Това нямаше как да стане ако втория ред беше Dog animal = null;

А също така може да премахнем case-ването и съвсем динамично на база потребитебския вход да вдигнем инстанция на куче или котка, а вопследствие ако се появи заек да не трябва да пипаме кода:

        string animalType = Console.ReadLine();
        Animal animal = (Animal)Activator.CreateInstance(Type.GetType(animalType));
        animal.A();
        // после извикваме някакъв метод от Животно
        // после друг, но никога специфичен за Котка/Куче
        // после например подаваме променливата на друг метод да прави нещо с нея

Мантрата в обектно-ориентирания дизайн като цяло е, че се опитваме да пишем такъв дизайн на кода, в който с минимум промени правим максимум резултати. Ако се налага да минем през целия код и да променим всичко, за да работи "подмяната", значи не е имало нужда да си усложняваме живота с класове и обекти и можехме просто да си редим ред след ред код. Така или иначе след време тези редове щяха да се сменят с други.

 

3
16/01/2017 16:14:52
dobri19 avatar dobri19 1 Точки

Да, с първото си изречение отговори на моя въпрос за конкретния ми пример. 

Точка 1 е ясна.

Точка 3 явно е, че имаме предимство при така дефинирани методи.

Точка 2 и 4 , разбирам, че имаме по-бърза промяна, ако се наложи или да ограничим бъдещи грешки, наши или чужди в този код. Единствено да кажем за 4 точка примера, ако искаме все пак да имаме достъп до SpecificDogMethod(), и сме съзадали Animal animal = new Dog(), как ще го направим?

0
dobri19 avatar dobri19 1 Точки

 Аз сам ще си отговоря на предния въпрос :))), очевидно трябва да кастваме обекта към наследника чийто метод искаме да използваме. ((Dog)animal).SpecificDogMethod(); или да използваме is или as.

0
RoYaL avatar RoYaL Trainer 6849 Точки

Да, но кастването е нещо, което ако правиш, значи правиш нещо грешно. Не би трябвало да ти се налага да кастваш. Кастът ще работи само ако преди това си инициализирал Куче. Ако си го сменил с котка - няма. Казано с по-малко думи: Не би трябвало да ти се налага да имаш достъп до специфични за конкретен клас методи, а само до такива от абстрактен клас или интерфейс.

Animal animal = new Dog();
//
//
//
//
//
((Dog)animal).SpecificDogMethod(); // works 

Но ако смениш в един момент животното да е котка - голям проблем:

Animal animal = new Cat();
//
//
//
//
//
((Dog)animal).SpecificDogMethod(); // does not work 

 

 

0
dobri19 avatar dobri19 1 Точки

 Здравейте, имам "един" доуточняващ въпрос по същата тема. Нека имаме същия пример, абстрактен клас Animal с абстрактни методи, и наследник Dog  с имплементация. Нека имаме и Animal dogBig = new Dog() и Dog dogSmall = new Dog(). Да направим и колекция List<Animal> animals = new List<Animal>(). Въпроса ми е защо компилаторът позволява и двата обекта да ги вкарам в листа? animals.Add(dogBig) и animals.Add(dogSmall). И третира ли ги по някакъв различен начин спрямо начинът им на създаване? Веднъж вкаран вътре в листа Dog dogSmall = new Dog(), става ли еквивалентен на другите обекти в листа които са инициализирани през базовия клас. И при извикване на методите при обхождане на листа компилаторът директно ли вика неговия метод или минава през базовия клас и съответно с виртуални таблици и пойнтъри търси конкретния метод както на dogBig? А когато създаваме Animal dogBig = new Dog() , компилатирът(compile-time) го третира като Animal, но всъщност знае че е Dog преди дори да викаме негови функции(run-time), но просто не ни позволява да ползваме директно методите му(compile-time), така ли е?

0
22/01/2017 10:09:33
Можем ли да използваме бисквитки?
Ние използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Можете да се съгласите с всички или част от тях.
Назад
Функционални
Използваме бисквитки и подобни технологии, за да предоставим нашите услуги. Използваме „сесийни“ бисквитки, за да Ви идентифицираме временно. Те се пазят само по време на активната употреба на услугите ни. След излизане от приложението, затваряне на браузъра или мобилното устройство, данните се трият. Използваме бисквитки, за да предоставим опцията „Запомни Ме“, която Ви позволява да използвате нашите услуги без да предоставяте потребителско име и парола. Допълнително е възможно да използваме бисквитки за да съхраняваме различни малки настройки, като избор на езика, позиции на менюта и персонализирано съдържание. Използваме бисквитки и за измерване на маркетинговите ни усилия.
Рекламни
Използваме бисквитки, за да измерваме маркетинг ефективността ни, броене на посещения, както и за проследяването дали дадено електронно писмо е било отворено.