2010-07-24 4 views
4

В моей игре у меня есть базовый класс Loot, который имеет методы, универсальные для всего, что может быть захвачено игроком и сохранено в его инвентаре. Это будет включать зелья, оборудование, боеприпасы и т. Д. Я могу экипировать стрелы, но не зелья. Таким образом, Arrow будет подклассом Ammo, который в конечном итоге будет получен от Loot. Я могу пить зелье, но не стрелу. Таким образом, Potion будет подклассифицировать Loot, но реализовать IConsumeable. И так далее.Метод BaseClass, который возвращает произвольный подкласс BaseClass

Объекты лута имеют свойство Количество (10 стрел, 2 зелья). В моем классе Loot у меня есть метод Split, который позволяет игроку взять «стек» предметов (например, стрелки) и разделить его на два отдельных стека. Поэтому он уменьшает количество экземпляров экземпляра на определенную сумму, а затем возвращает новый экземпляр Arrow с значением Quantity = =, который был взят из исходного экземпляра.

Моя идея заключалась в том, что я напишу метод в Loot, так как любой Loot может быть уложен в стек, если его свойство int StackLimit больше 1. После уменьшения вызывающего лута на указанное количество я верну новый объект того же типа. Проблема в том, что я не знаю, какой тип подкласса Loot будет объектом.

public abstract class Loot 
{ 
    public int Quantity { get; set; } 
    public Loot Split(int quantityToTake) 
    { 
     Loot clone = (Loot)this.MemberwiseClone(); 
     //RestrictNumberToRange(int min, int max, int value) is a delegate which clamps a value to min,max 
     this.Quantity -= Utility.RestrictNumberToRange<int>(1, this._quantity - 1, quantityToTake); 
     clone.Quantity = quantityToTake; 
     return clone; 
    } 
}

Действительно ли это плохой способ? Я думал о Отражении, но я слышу смешанные мнения о том, использовать или не использовать его в таком случае.

Невозможно определить способ борьбы с этим. Самый большойSubClass?

Я знаю, что у моих подклассов могут быть разные конструкторы, поэтому, вероятно, нецелесообразно пытаться возвратить «new this.FurthestSubclass()», потому что я не знаю, как его построить. Но я бы хотел иметь дело с его методами Loot, поэтому я использую Loot для возвращаемого типа.

ответ

1

Я думаю, что это хороший пример для дженериков. Попробуйте переписать метод разделения в Loot следующим образом:

public TLoot Split<TLoot>(int quantityToTake) where TLoot : Loot 
{ 
    TLoot clone = (TLoot)this.MemberwiseClone(); 
    ... 
    return clone; 
} 

Это должно позаботиться о наборе вопросов.

РЕДАКТИРОВАТЬ ДОБАВИТЬ: проблема с конструктором немного интереснее, но вы можете найти бесцельные конструкторы, полезные в сочетании с инициализаторами объектов. Если изменить ограничение следующим образом:

public TLoot Split<TLoot>(int quantityToTake) where TLoot : Loot, new(TLoot) 
{ 
    // stuff 
    TLoot newLoot = new TLoot(); 
    ... 
    return newLoot; 
} 

«Новый (T)» ограничение позволяет создавать новые объекты на основе общего типа.

ДАЛЕЕ EDIT: я должен дать пример инициализатора объекта в этом контексте:

TLoot newLoot = new TLoot { Quantity = quantityToTake }; 

Это предполагает, что Loot имеет общественное свойство Количество. Инициализаторы объектов могут устанавливать значения для любой публичной собственности, имеющей общественность set{};

+0

Знаете, я только что кое-что объяснил мне недавно по этим строкам, и я подумал, что получил. Ясно, что я еще не достиг Дзен. Спасибо, спасибо. Итак, чтобы быть ясным: если этот экземпляр - это Стрела, которая происходит от Ammo, которая происходит от Loot, метод Split вернет Arrow? – 2010-07-24 21:23:15

+0

@ Супер, да, это правильно. Дженерики абсолютно замечательные! –

+0

Как я уже сказал, я видел это раньше (на самом деле делегат в моем примере использует его), но, увидев, что он применяется здесь, действительно открыл глаза - спасибо, человек. – 2010-07-24 21:30:25

1

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

Arrow arrow1; 
/* some code initializing arrow1 */ 
Arrow arrow2 = arrow1.Split<Arrow>(10); 

Очевидно, что arrow1 будучи Arrow, мы не могли бы написать что-нибудь еще (как, например, Arrow b = a.Split<Potion>(10);, который не имеет смысла), но Arrow arrow2 = arrow1.Split(10);, к сожалению, не будет компилироваться, так как тип возвращаемого универсального метода не может быть выведено, если у него нет параметров того же типа.

Трудностью для этого делать: Split Способ продления: Loot. :-)

Его прототип становится public static TLoot Split<TLoot>(this TLoot item, int quantityToTake) where TLoot : Loot и TLoot теперь имеет место среди параметра в позиции 1 (даже если введено специальным ключевым словом this: nevermind!). Этот первый параметр исчезает при вызове, так что все происходит так, как если бы мы возвращали тип вывода.

Теперь вы можете написать Arrow arrow2 = arrow1.Split(10);. Это совершенно законно, и возвращаемый тип является подлинным строго типизированным Arrow (вы даже можете написать var arrow2 = arrow1.Split(10);, а затем проверить тип arrow2).

Я признаюсь, что в этом контексте кажется более крутым и элегантным, чем действительно полезным. Но я увлекаюсь свободным интерфейсом (метод цепочки), и там это действительно становится огромным улучшением. Сравните эти 2 заявления:

/*Given an certain among of arrows found in the treasure chamber, 
    each player in the team receives a fraction of it according to his age 
    and experience, but always 10 arrows at least anyway. 
    Ok, ok ! I had to search before finding such an example vaguely accurate 
    in the context of arrows and potions ! lol*/ 
foreach(Player p in Team) 
    p.Arrows = Treasure.Arrows.Split<Arrow>(10).Split<Arrow>(p.Age).Split<Arrow>(p.Experience*2); 

foreach(Player p in Team) 
    p.Arrows = Treasure.Arrows.Split(10).Split(p.Age).Split(p.Experience*2); 

Ok, здесь преимущество остается бедными, но там действительно бывают ситуации, когда сделать тип возвращаемого неявным чрезвычайно улучшает readibility (Treasure.Arrows.Split<Arrow>(10+p.Age+p.Experience*2) работ, а также и короче!).

Смежные вопросы