Връщането в метод на базов клас се връща в повикване на дъщерен клас?

Създавам C# рамка, в която (почти) всичките ми класове ще бъдат базирани на единичен абстрактен базов клас. Този базов клас съдържа някои частно зададени свойства, едно от които е булевото Garbage.

В цикъла за рисуване на XNA не искам дъщерни класове да изпълняват кода в съответните си методи Draw(), ако свойството Garbage на базовия клас е зададено на true. Опитах се да направя това със следната реализация:

Абстрактен базов клас:

public virtual void Draw(GameTime GameTime, SpriteBatch SpriteBatch)
{
    if (Garbage) return;
}

Наследен клас:

public void Draw(GameTime GameTime, SpriteBatch SpriteBatch, Color OverlayColor, float Scale)
{
    base.Draw(GameTime, SpriteBatch);

    //Other code here...
}    

Тъй като методът Draw на базовия клас се извиква преди действителния код в моя дъщерен клас, той удря този оператор return, който бих искал да проследя в извикването Draw() на моя дъщерен клас. Това обаче не се случва.

Има ли някакъв начин да постигна ефекта, който искам, без да добавям, че "ако (Гарбадж) се върне;" ограничение до върха на метода Draw() на всеки наследен клас?


person William Thomas    schedule 18.01.2012    source източник
comment
Това всъщност не е начинът, по който работи наследяването. Предлагам ви да опитате друг начин да направите това. Може би можете да използвате същия метод, но с параметри по подразбиране?   -  person Oskar Kjellin    schedule 18.01.2012
comment
Забравихте ли да дефинирате метода като заместващ в наследения клас или това е просто печатна грешка?   -  person Christopher Currens    schedule 18.01.2012
comment
@ChristopherCurrens Не са същите параметри, не могат да бъдат отменени   -  person Oskar Kjellin    schedule 18.01.2012
comment
@OskarKjellin - Да, със сигурност не може! Това е, което получавате, когато погледнете само имената на методите :D   -  person Christopher Currens    schedule 19.01.2012


Отговори (6)


Едно нещо, което може да обмислите, е да имате два метода. Можете да имате своя метод Draw на абстрактната база, която не е отменена, и след това да дефинирате абстрактен защитен метод, наречен PerformDraw(), който вашите наследници отменят.

Чертежът на абстрактна основа изглежда така:

public void Draw(GameTime GameTime, SpriteBatch SpriteBatch)
{
    if (!Garbage) PerformDraw(GameTime, SpriteBatch);
}

Това ви позволява да замените поведението на теглене, но принуждава всички наследници да използват проверката за "боклук".

person Erik Dietrich    schedule 18.01.2012
comment
Това ще работи, освен че изглежда, че всеки извлечен Draw() има различен набор от параметри. - person svick; 18.01.2012
comment
Да, това е добра точка. Не бях сигурен какво да правя с претоварването, тъй като публикацията изглежда предполагаше връзка на наследяване с този метод. Така че, аз просто предлагам един вид широк модел тук. - person Erik Dietrich; 19.01.2012
comment
Благодаря за този трик @Erik. Жалко е, че трябва да добавя нов метод PerformDraw към всеки от моите дъщерни класове, но въпреки това работи доста добре. По отношение на различните параметри, всъщност замествах метода Draw(GameTime, SpriteBatch) на основния клас с метод в дъщерния клас със същите параметри. Този отменен метод в дъщерния клас извика метода на основния клас чрез извикване base.Draw(GameTime, SpriteBatch), последвано от извикване на претоварен метод (който беше този, който първоначално публикувах по погрешка) Draw(GameTime, SpriteBatch, Color , плаващ). - person William Thomas; 19.01.2012

Не, няма начин чрез наследяване/полиморфизъм да постигнете това, което искате. Това, което бих предложил, е да имате Entity Manager, който съдържа всичките ви обекти, които искат да рисуват, и може да проследи през всеки, казвайки нещо в следния смисъл на:

foreach (IEntity entity in _entities)
{
       if(!entity.Garbage)
            entity.Draw(....);
}

По този начин, ако се извика draw, обектът може да прави това, което трябва, а боклукът се премества на различно ниво и му позволява да бъде гъвкав, така че ако по-късно имате нужда от добавяне на друга клауза, преди даден обект да може да рисува, трябва само да промените 1 ред код, а не всеки метод за рисуване и основно получете ефекта, към който първоначално сте се стремили.

person OnResolve    schedule 18.01.2012

Ако изобщо е възможно, може да помислите за Образец на шаблонен метод. Нека основният метод е следният:

public void Draw(GameTime gameTime, SpriteBatch spriteBatch, Color overlayColor, float scale)
{
    if (Garbage) return;

    DrawMe(gameTime, spriteBatch, overlayColor, scale);
}

protected abstract DrawMe(GameTime gameTime, spriteBatch SpriteBatch, Color overlayColor, float scale);

С всеки дъщерен клас, прилагащ необходимия метод DrawMe (несъмнено лошо име), вие получавате потока, който искате, и конкретните класове само знаят как да рисуват сами.

person Austin Salonen    schedule 18.01.2012
comment
Връзката няма да работи, докато Wikipedia не приключи с протеста си. - person Austin Salonen; 19.01.2012
comment
Има няколко други начина, по които човек може да го направи, но всъщност въпросът за ОП просто крещи този модел. - person Jon Hanna; 19.01.2012

Ако искате вашият дъщерен клас да спаси извикването на функция въз основа на някаква ситуация в родителския клас при void връщане на повикване, имате следните възможности за избор:

1) Проверете защитена / публична флагова променлива във вашия дъщерен метод и след това разклонете логиката. още известен като

// base class
if (Garbage) _someFlag = true;

// child class

base.Draw();

if(!_someFlag) // Do Stuff

2) Хвърлете изключение във вашия родителски клас, което спира изпълнението на останалата част от функцията (ако приемем, че това е подходящо за вашата ситуация).

Във вашия конкретен случай се опитвате да върнете данни от родителския клас към дъщерния клас чрез метод void. Ако това бях аз, щях да направя нещо подобно:

// base class
public bool IsGarbage { get; set; }

// child class

public void Draw()
{
     if(!IsGarbage)
     {
         // Do Stuff
     }
}
person Tejs    schedule 18.01.2012

Е, само пример:

public class BaseClass 
{
   public bool Garbage {get;set}
}

//somewhere you have a collection of all artifacts to draw (children of BaseClass) 
Lis<BaseClass> stuffToDraw = new List<BaseClass){....}

//and somewhere in the framewrok you have a method to draw them  

public void Paint(...)
{
     stuffToDraw.ForEach(stuff=>
     {
         if(stuff.Garbage) return; 
         stuff.Draw(....); //call of overriden virtual method
     });
}

По този начин имате наедно място, където да проверите дали да нарисувате артефакт или не.

Надявам се това да помогне.

person Tigran    schedule 18.01.2012

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

Пример за моя метод на рисуване

public override void Draw(List<string> menuOptions, int currentSelection, MenuType type)
{
    Draw("", menuOptions, currentSelection, type, Color.White, Color.Gray, Color.WhiteSmoke, Color.DimGray);
}

public void Draw(string menuName, List<string> menuOptions, int currentSelection, MenuType type)
{
    Draw(menuName, menuOptions, currentSelection, type, Color.White, Color.Gray, Color.WhiteSmoke, Color.DimGray);
}

public void Draw(string menuName, List<string> menuOptions, int currentSelection, MenuType type, Color foreNorm, Color backNorm, Color foreSelect, Color backSelect)
{
    //Actual Draw
}
person MyKuLLSKI    schedule 18.01.2012