Преобразование из базового в производный объект с огромным определением конструктора - PullRequest
0 голосов
/ 30 октября 2018

Допустим, у меня есть класс BaseClass

class BaseClass
{
    public int PropertyOne { get; }

    public string PropertyTwo { get; }

    public BaseClass(int propertyOne, string propertyTwo)
    {
        PropertyOne = propertyOne;
        PropertyTwo = propertyTwo;
    }
}

и дочерний класс DerivedClass

class DerivedClass : BaseClass
{
    public DerivedClass(int propertyOne, string propertyTwo) : base(propertyOne, propertyTwo)
    {
    }

    public void DerivedMethod()
    {

    }

    public static DerivedClass ConvertFromBaseClass(BaseClass baseClass)
    {
        return new DerivedClass(baseClass.PropertyOne, baseClass.PropertyTwo); // <-- ?
    }
}

Можно ли реализовать DerivedClass.ConvertFromBaseClass без явного вызова конструктора, возможно, с использованием отражения? В действительности эти классы имеют более 50 аргументов, передаваемых в конструктор, и поэтому крайне неудобно и подвержено ошибкам писать вызов конструктора в методе преобразователя.

Ответы [ 3 ]

0 голосов
/ 30 октября 2018

В зависимости от ситуации вы можете реструктурировать свой код, чтобы использовать вместо него Mixins.

Сначала вы извлекаете интерфейс из базового класса. Решарпер может сделать это одним нажатием кнопки:

class BaseClass : Interface
{
    public int PropertyOne { get; }

    public string PropertyTwo { get; }

    public BaseClass(int propertyOne, string propertyTwo)
    {
        PropertyOne = propertyOne;
        PropertyTwo = propertyTwo;
    }
}

interface Interface
{
    int PropertyOne { get; }

    string PropertyTwo { get; }
 }

Затем вместо наследования от базового класса вы добавляете поле в производный класс, которое вы используете для реализации интерфейса. Resharper также может сделать это в экземпляре:

class DerivedClass : Interface
{
    private readonly Interface _interface;

    public DerivedClass(Interface interface) => _interface = Interface;

    public int PropertyOne => _interface.PropertyOne;

    public string PropertyTwo => _interface.PropertyTwo;
}

Затем вы просто передаете экземпляр базового класса в конструктор.

В этом есть два преимущества. Во-первых, это означает, что вам не нужно переопределять логику в конструкторе и преобразователе, чтобы иметь дело со всеми 50 параметрами. Фактически Решарпер может сделать всю логику за вас.

Во-вторых, это означает, что вам не нужно копировать 50 параметров (около 200 байтов в 32-битной среде выполнения, 400 байтов в 64-битной) каждый раз, когда вы создаете производный тип из базового класса.

Это может или не может быть подходящим шаблоном, в зависимости от вашей ситуации

0 голосов
/ 30 октября 2018

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

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

static class ExtensionMethods
{
    public static string ToPascalCase(this string camelCase)
    {
        return string.IsNullOrEmpty(camelCase)
            ? camelCase
            : camelCase.First().ToString().ToUpper() + camelCase.Substring(1);
    }
}

class BaseClass
{
    public string Able { get; set; }
    public int Baker { get; set; }
    public DateTime Candy { get; set;}

    static public DerivedClass CreateDerived(BaseClass source)
    {
        var constructor = typeof(DerivedClass).GetConstructors()[0];
        var parameters = constructor.GetParameters();
        var arguments = parameters.Select
            (
                param => source
                    .GetType()
                    .GetProperty(param.Name.ToPascalCase())
                    .GetValue(source, null)
            );
        var instance = constructor.Invoke(arguments.ToArray());
        Console.WriteLine("{0} arguments found", arguments.Count());
        return (DerivedClass)instance;
    }
}

class DerivedClass : BaseClass
{
    public DerivedClass(string able, int baker, DateTime candy)
    {
        Able = able;
        Baker = baker;
        Candy = candy;
    }
}

public class Program
{
    public static void Main()
    {
        var b = new BaseClass
        {
            Able = "Able!",
            Baker = 2,
            Candy = new DateTime(2018,10,31)
        };

        var d = BaseClass.CreateDerived(b);

        Console.WriteLine("Able={0} Baker={1} Candy={2}", d.Able, d.Baker, d.Candy);
    }
}

Выход:

3 arguments found
Able=Able! Baker=2 Candy=10/31/2018 12:00:00 AM

Ссылка на рабочий пример на DotNetFiddle

0 голосов
/ 30 октября 2018

Краткий ответ: нет. Учитывая классы, как вы их представили, нет никакого ключевого слова компилятора или синтаксического сахара для автоматического отображения.

Более длинный ответ: тот факт, что вы сталкиваетесь с этой проблемой в первую очередь, свидетельствует о том, что ваши классы, вероятно, не структурированы оптимальным образом. Если у вас есть свобода менять классы в первую очередь, вы, вероятно, можете получить результаты, которые вы ищете.

Например, если у вас есть более 50 аргументов конструктора, которые всегда абсолютно одинаковы и используются многими различными подклассами, почему бы не объявить один класс, который имеет все эти аргументы и свойства, и сделать основу класс принимает один из них в качестве аргумента одного конструктора?

public DerivedClass(BaseClassArguments args) : base(args)

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

Или вы можете заставить эти классы принимать "базовый" класс в качестве аргумента конструктора и сохранять его как поле или свойство, а не расширять базовый класс. Это известно как использование композиции вместо наследования.

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