Способ преобразования базового типа в производный тип

StackOverflow https://stackoverflow.com/questions/124336

Вопрос

Я не уверен, странно ли это делать или нет, или это какой-то кодовый запах ... но мне было интересно, есть ли способ (какой-нибудь шаблон ооп был бы хорош) "привести" базовый тип к форме его производного типа.Я знаю, что в этом мало смысла, поскольку производный тип будет обладать дополнительной функциональностью, которую не предлагает родительский, что само по себе не является фундаментально обоснованным.Но есть ли какой-то способ сделать это?Вот пример кода, чтобы я мог лучше объяснить, о чем я спрашиваю.

public class SomeBaseClass {
    public string GetBaseClassName {get;set;}
    public bool BooleanEvaluator {get;set;}
}

public class SomeDerivedClass : SomeBaseClass {
    public void Insert(SqlConnection connection) {
          //...random connection stuff
          cmd.Parameters["IsItTrue"].Value = this.BooleanEvalutar;
          //...
    }
}

public static void Main(object[] args) {
    SomeBaseClass baseClass = new SomeBaseClass();
    SomeDerivedClass derClass = (SomeDerivedClass)baseClass; 
    derClass.Insert(new sqlConnection());
}

Я знаю, это кажется глупым, но есть ли какой-нибудь способ добиться чего-то подобного?

Это было полезно?

Решение

Не очень грамотно, на "управляемых" языках.Это понижающий коэффициент, и нет разумного способа справиться с этим именно по той причине, которую вы описали (подклассы предоставляют больше, чем базовые классы - откуда берется это "больше"?).Если вы действительно хотите аналогичное поведение для определенной иерархии, вы могли бы использовать конструкторы для производных типов, которые будут использовать базовый тип в качестве прототипа.

Можно было бы создать что-то с помощью reflection, которое обрабатывало бы простые случаи (более конкретные типы, у которых нет состояния добавления).В общем, просто переделайте дизайн, чтобы избежать этой проблемы.

Редактировать:Упс, не могу написать операторы преобразования между базовыми / производными типами.Странность Microsoft, пытающейся "защитить вас" от самих себя.Ну что ж, по крайней мере, они и близко не такие вредные, как Солнце.

Другие советы

Попробуйте композицию вместо наследования!

Мне кажется, что вам было бы лучше передать экземпляр SomeBaseClass в SomeDerivedClass (который больше не будет производным базового класса и должен быть переименован как таковой)

public class BooleanHolder{       
    public bool BooleanEvaluator {get;set;}
}

public class DatabaseInserter{
    BooleanHolder holder;

    public DatabaseInserter(BooleanHolder holder){
        this.holder = holder;
    }

    public void Insert(SqlConnection connection) {
          ...random connection stuff
          cmd.Parameters["IsItTrue"].Value = holder.BooleanEvalutar;
          ...
    }
}

public static void Main(object[] args) {
    BooleanHolder h = new BooleanHolder();
    DatabaseInserter derClass = new DatabaseInserter(h);
    derClass.Insert(new sqlConnection);
}

Проверьте http://www.javaworld.com/javaworld/jw-11-1998/jw-11-techniques.html (страница 3):

Повторное использование кода с помощью composition Композиция предоставляет Apple альтернативный способ повторного использования реализации Fruit для peel().Вместо расширения Fruit, Apple может содержать ссылку на экземпляр Fruit и определять свой собственный метод peel() который просто вызывает peel() для Fruit.

Лично я не думаю, что в данном случае стоит заморачиваться с использованием наследования.Вместо этого просто передайте экземпляр базового класса в конструктор и получите доступ к нему через переменную-член.

private class ExtendedClass //: BaseClass - like to inherit but can't
{
    public readonly BaseClass bc = null;
    public ExtendedClass(BaseClass b)
    {
        this.bc = b;
    }

    public int ExtendedProperty
    {
        get
        {
        }
    }
}

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

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

SomeDerivedClass layer = JsonConvert.DeserializeObject<SomeDerivedClass>(JsonConvert.SerializeObject(BaseClassObject));

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

Ты сам сказал, что это кажется глупым:

SomeBaseClass class = new SomeBaseClass();
SomeDerivedClass derClass = (SomeDerivedClass)class; 

Так что спросите себя, является ли class на самом деле это экземпляр SomeDerivedClass?Нет, поэтому преобразование не имеет смысла.Если вам нужно преобразовать SomeBaseClass Для SomeDerivedClass, затем вы должны предоставить какой-то вид преобразования, либо конструктор, либо метод преобразования.

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

Язык C # не допускает таких операторов, но вы все равно можете написать их, и они работают:

[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Implicit(Base a) { ... }

[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Explicit(Base a) { ... }

Да, это запах кода, и он в значительной степени подтверждает тот факт, что ваша цепочка наследования нарушена.

Мой догадываюсь (из ограниченной выборки) заключается в том, что вы предпочли бы, чтобы DerivedClass работал с экземпляром SomeBaseClass - так что "DerivedClass имеет SomeBaseClass", а не "Производный класс является SomeBaseClass".Это известно как "предпочтение композиции перед наследованием".

Как отмечали другие, кастинг, который вы предлагаете, на самом деле невозможен.Может быть, это будет тот случай, когда Шаблон декоратора(Выдержка из первой главы) может быть введена?

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

Это называется downcasting, и предложение Seldaek использовать "безопасную" версию является обоснованным.

Вот довольно приличный описание с примерами кода.

Это невозможно, потому что как вы собираетесь получить "лишнее", которое есть у производного класса?Как компилятор узнает, что вы имеете в виду derivedClass1, а не derivedClass2, когда вы создаете его экземпляр?

Я думаю, что то, что вы действительно ищете, - это фабричный шаблон или что-то подобное, чтобы вы могли создавать экземпляры объектов, на самом деле не зная явного типа, который создается.В вашем примере наличие метода "Insert" было бы интерфейсом, который реализует экземпляр factory returns.

Я не знаю, почему никто этого не сказал, и, возможно, я что-то пропустил, но вы можете использовать ключевое слово as, и если вам нужно выполнить оператор if, используйте if.

SomeDerivedClass derClass = class as SomeDerivedClass; //derClass is null if it isnt SomeDerivedClass
if(class is SomeDerivedClass)
    ;

-редактировать- Я задал этот вопрос давным-давно

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

Я решил эту проблему, применив пустой конструктор к классам DTO, используя его следующим образом:

class InternalDbType {
    public string Name { get; set; }
    public DateTime Date { get; set; }
    // Many more properties here...
}

class SimpleDTO {
    public string Name { get; set; }
    // Many more properties here...
}

class ComplexDTO : SimpleDTO {
    public string Date { get; set; }
}

static class InternalDbTypeExtensions {
    public static TDto ToDto<TDto>(this InternalDbType obj) where TDto : SimpleDTO, new() {
        var dto = new TDto {
            Name = obj.Name
        }
    }
}

Затем я могу повторно использовать логику преобразования из простого DTO при преобразовании в сложный.Конечно, мне придется заполнить свойства сложного типа каким-то другим способом, но со многими-многими свойствами простого DTO это действительно упрощает ситуацию, IMO.

Это не сработает.Перейдите на страницу справки, на которую ссылается ошибка компиляции.

Лучшее решение - использовать здесь заводские методы.

Как указывалось во многих ответах, вы не можете опускать руки, что имеет полный смысл.

Однако в вашем случае, SomeDerivedClass не имеет свойств, которые будут "отсутствовать".Таким образом, вы могли бы создать метод расширения, подобный этому:

public static T ToDerived<T>(this SomeBaseClass baseClass) 
    where T:SomeBaseClass, new()
{
    return new T()
    {
        BooleanEvaluator = baseClass.BooleanEvaluator,
        GetBaseClassName = baseClass.GetBaseClassName
    };
}

Таким образом, вы не проводите кастинг, а просто конвертируете:

SomeBaseClass b = new SomeBaseClass();
SomeDerivedClass c = b.ToDerived<SomeDerivedClass>();

Это действительно работает только в том случае, если все данные в базовом классе представлены в виде читаемых и записываемых свойств.

C ++ обрабатывает это с помощью конструктора. Приведение типов C++.Мне это кажется недосмотром.Многие из вас поднимали вопрос о том, что этот процесс будет делать с дополнительными свойствами.Я бы ответил, что делает компилятор, когда создает производный класс, когда программист не задает свойства?Я разобрался с этой ситуацией аналогично C ++.Я создаю конструктор, который принимает базовый класс, затем вручную устанавливаю свойства в конструкторе.Это определенно предпочтительнее, чем устанавливать переменную в производном классе и нарушать наследование.Я бы также предпочел его заводскому методу, потому что я думаю, что результирующий код выглядел бы более чистым.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top