Возврат в методе базового класса возвращается в вызове дочернего класса?

Я создаю структуру С#, в которой (почти) все мои классы будут основаны на одном абстрактном базовом классе. Этот базовый класс содержит несколько частных свойств, одним из которых является логическое Мусор.

В цикле отрисовки 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() моего дочернего класса. Однако этого не происходит.

Есть ли способ добиться желаемого эффекта, не добавляя, что «if (Garbage) return;» ограничение на верхнюю часть метода 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 через каждый сказать что-то в результате:

foreach (IEntity entity in _entities)
{
       if(!entity.Garbage)
            entity.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
Ссылка не будет работать, пока Википедия не закончит свой протест. - 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