Способ приведения базового типа к производному типу

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

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());
}

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

В этом вопросе неправильно использовать термин «отливка». Приведение подразумевает, что отбрасываемый объект уже является объектом этого типа. Например: объект o = 3; MethodWithIntegerParam ((int) o); В вашем вопросе baseClass не создается с производным классом. Итак, вы ищете конверсию, а не кастинг.

AXMIM 07.05.2015 15:45
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
47
1
67 440
17
Перейти к ответу Данный вопрос помечен как решенный

Ответы 17

Ответ принят как подходящий

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

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

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

В этом случае нельзя использовать операторы приведения / преобразования.

leppie 24.09.2008 02:39

Ah well, at least they're no where near as bad as Sun. - что ты имеешь в виду? Просто из любопытства; Я не знаю Java. (Я знаю, что прошло 4 года.)

Konrad Morawski 25.08.2012 01:02

@KonradMorawski Я имел в виду особенности языка Java, разработанные для обеспечения того, чтобы вы писали "хороший" код (где "хорошее" их мнение). Больше всего меня ненавидят проверяемые исключения (так что вы не могу просто игнорируете исключения, когда-либо).

Adam Wright 25.08.2012 17:15

«Ой, нельзя писать операторы преобразования между базовыми / производными типами». .Net позволяет это сделать, и это можно сделать с помощью C#. См. stackoverflow.com/a/15987692/1497385

Ark-kun 13.04.2013 16:20

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

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

Лол ... Я знаю, что пытался передать свой мыслительный процесс ... поверьте мне, я пытался: D

Adam Driscoll 24.09.2008 02:38

«Это не работает :)» .Net позволяет это, и это возможно с помощью C#. См. stackoverflow.com/a/15987692/1497385

Ark-kun 13.04.2013 16:19

Это называется опровержением, и предложение Селдэка использовать «безопасную» версию разумно.

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

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

Интересно, что когда вы «обращаете» восходящее преобразование, вся «лишняя» полученная информация снова становится доступной. Ясно, что он каким-то образом сохранился в куче памяти.

Chris Johnson 10.10.2013 13:35

Я читал об этой проблеме. Причина, по которой информация сохраняется на этом объекте, заключается в том, что сам объект не изменяется при его повышении, а только ссылка на него. И RE этот ответ кажется вполне законным поводом для того, чтобы опускать руки. Особенно при работе с SOA.

Craig Brett 09.04.2014 17:18

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

Вы сами сказали, что это глупо:

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

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

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

«Нет, это невозможно. На управляемом языке, таком как C#, это просто не сработает». .Net позволяет это сделать, и это можно сделать с помощью C#. См. stackoverflow.com/a/15987692/1497385

Ark-kun 13.04.2013 16:18

Это невозможно, потому что как вы собираетесь получить «лишнее», которое есть у производного класса. Как компилятор узнает, что вы имеете в виду производный класс1, а не производный класс2, когда вы создаете его экземпляр?

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

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

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

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

Мне кажется, что вам лучше передать экземпляр 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):

Code reuse via composition Composition provides an alternative way for Apple to reuse Fruit's implementation of peel(). Instead of extending Fruit, Apple can hold a reference to a Fruit instance and define its own peel() method that simply invokes peel() on the Fruit.

Хотя я предпочитаю композицию наследованию, идея о том, что «Яблоко есть плод», а не «Яблоко - это плод», является худшим примером, который я когда-либо видел. Если Apple определяет свой собственный метод Peel, то он больше не может быть заменен на Fruit, что коренится во всей концепции Fruit.

Mark Brackett 25.09.2008 03:10

Вместо «Яблоко есть фрукты» вы могли бы прочитать это как «Яблоко обладает свойствами фрукта», что звучит намного лучше и на самом деле точнее, чем «Яблоко - это фрукт».

intrepidis 20.12.2014 00:58

Я бы сказал, что «свойство быть плодом» не будет ни композицией, ни наследованием, а реализацией интерфейса :)

HaMster 06.12.2017 13:45

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

.Net позволяет это сделать, и это можно сделать с помощью C#. См. stackoverflow.com/a/15987692/1497385

Ark-kun 13.04.2013 16:19

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

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

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

-edit- Я задал этот вопрос давно

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

Недавно мне понадобилось расширить простой 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.

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

SomeDerivedClass layer = JsonConvert.DeserializeObject<SomeDerivedClass>(JsonConvert.SerializeObject(BaseClassObject));
«Я не говорю, что рекомендую это».
twip 16.02.2017 03:01

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

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
        {
        }
    }
}

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

Ben Winding 16.12.2015 14:41

Но в этом случае вызвать методы базового класса невозможно, так как в ExtendedClass их вообще нет.

user2440074 06.05.2016 11:53

Да, вы можете: extendedObject.BaseClass.BaseClassMethod (args)

Rob Sedgwick 06.05.2016 12:31

Это похоже на паттерн "Декоратор", который в данном случае подойдет, и его стоит поискать в Google! У шаблонов дизайна есть большинство ответов !!

dreadeddev 10.05.2017 12:14

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

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

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

как будет работать неявная операция, если вы назначите List <Derived> = List <Base>?

Boppity Bop 12.08.2016 19:07

@BoppityBop будет работать так же, как List<long> = List<int> или List<int> = List<long> - не сработает. Однако вы можете создать свою собственную коллекцию с некоторыми операторами неявного преобразования.

Ark-kun 13.08.2016 02:02

просто быть чистым. Вы говорите, что op_Implicit в соответствии с вашим решением не будет называться, когда базовые / производные находятся в общей коллекции. Правильно?

Boppity Bop 15.08.2016 14:48

@BoppityBop Да. Операторы статичны. Нет даже наследования операторов. Гораздо меньше «наследования оператора типа элемента универсального класса».

Ark-kun 18.08.2016 10:45

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

Однако в вашем случае 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>();

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

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