Как правильно копировать поля из производных классов? - PullRequest
0 голосов
/ 23 января 2019

давайте возьмем следующие классы в качестве примера:

базовый класс:

public class Spell
{
    public int castRange;

    public Spell Copy()
    {
        Spell spell = new Spell();
        spell.castRange = this.castRange;
        return spell;
    }
}

производный класс:

public class ManaSpell : Spell
{
    public int manaCost;

    public new ManaSpell Copy()
    {
        ManaSpell spell = new ManaSpell();
        spell.castRange = this.castRange;
        spell.manaCost = this.manaCost;
        return spell;
    }
}

Я не могу использовать virtual и override для метода Copy () , потому что они имеют разные типы возврата, поэтому я использую новое ключевое слово . Задача начинается со следующего класса:

public class Unit
{
    public Spell spell;

    public Unit(Spell spell)
    {
        // This will call the Copy method in the base class, even if the 
        // parameter is actually a ManaSpell

        this.spell = spell.Copy();

        // So instead I have to do a check first:

        if (spell is ManaSpell)
        {
            ManaSpell manaSpell = spell as ManaSpell;
            this.spell = manaSpell.Copy();
        }
    }
}

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

Есть ли лучший способ сделать это?

Ответы [ 2 ]

0 голосов
/ 23 января 2019

Простой способ создания клонов - использовать закрытый метод MemberwiseClone, унаследованный от System.Object.Преимущество заключается в автоматическом учете полей из производных классов.Т.е. вам не нужно извлекать метод копирования, чтобы он работал.

public class Spell
{
    public int castRange;

    public Spell ShallowClone()
    {
        return (Spell)MemberwiseClone();
    }

    public override string ToString() => $"castRange = {castRange}";
}

public class ManaSpell : Spell
{
    public int manaCost;

    public override string ToString() => $"castRange = {castRange}, manaCost = {manaCost}";
}

Этот тест ...

Spell spell = new ManaSpell { castRange = 5, manaCost = 10 };
var copy = spell.ShallowClone();
Console.WriteLine(copy);
Console.ReadKey();

... отображает

castRange = 5, manaCost = 10

Вы не можете избежать каста, если вам нужен результат, набранный как ManaSpell.


Возможное решение, позволяющее избежать кастинга, заключается виспользуйте общий статический метод.Компилятор C # может вывести тип возврата из статического (время компиляции) параметра типа.

public class Spell
{
    public int castRange;

    public Spell ShallowClone()
    {
        return (Spell)MemberwiseClone();
    }

    public override string ToString() => $"castRange = {castRange}";

    public static T ShallowClone<T>(T original)
        where T : Spell
    {
        return (T)original.ShallowClone();
    }
}

This ...

ManaSpell manaSpell = new ManaSpell { castRange = 6, manaCost = 18 };
ManaSpell manaSpellCopy = Spell.ShallowClone(manaSpell);
Console.WriteLine(manaSpellCopy);

... prints

castRange = 6, manaCost = 18

0 голосов
/ 23 января 2019

Если у вас нет действительно веской причины скрыть (это то, что new делает) для вашей Copy реализации базового класса, вы не должны new это делать.

Кажется, вам это вообще не нужно.Вы действительно хотите скопировать Spell, независимо от его фактического типа.Итак, пусть экземпляр разрешает вызов на Copy, что выполняется обычным переопределением:

public class Spell
{
    public int castRange;

    public virtual Spell Copy()
    {
        Spell spell = new Spell();
        spell.castRange = this.castRange;
        return spell;
    }
}
public class ManaSpell : Spell
{
    public int manaCost;

    public override Spell Copy()
    {
        ManaSpell spell = new ManaSpell();
        spell.castRange = this.castRange;
        spell.manaCost = this.manaCost;
        return spell;
    }
}

Теперь вы можете вызывать Copy для любого экземпляра Spell без необходимости различать фактический тип:

this.Spell = spell.Copy()

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...