Я не уверен, что это странно или нет, или это как-то пахнет кодом ... но мне было интересно, есть ли способ (какой-то шаблон 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());
}
Я знаю, что это кажется глупым, но есть ли способ добиться чего-то в этом роде?





Не добротно, на «управляемых» языках. Это понижение, и нет разумного способа справиться с этим именно по той причине, которую вы описали (подклассы предоставляют больше, чем базовые классы - откуда это «больше»?). Если вам действительно нужно аналогичное поведение для конкретной иерархии, вы можете использовать конструкторы для производных типов, которые будут принимать базовый тип в качестве прототипа.
Можно создать что-то с отражением, которое обрабатывало бы простые случаи (более конкретные типы, которые не имеют состояния добавления). В общем, просто сделайте редизайн, чтобы избежать проблемы.
Обновлено: Woops, нельзя писать операторы преобразования между базовыми / производными типами. Странность того, что Microsoft пытается «защитить вас» от самого себя. Ну, по крайней мере, они далеко не так плохи, как Солнце.
В этом случае нельзя использовать операторы приведения / преобразования.
Ah well, at least they're no where near as bad as Sun. - что ты имеешь в виду? Просто из любопытства; Я не знаю Java. (Я знаю, что прошло 4 года.)
@KonradMorawski Я имел в виду особенности языка Java, разработанные для обеспечения того, чтобы вы писали "хороший" код (где "хорошее" их мнение). Больше всего меня ненавидят проверяемые исключения (так что вы не могу просто игнорируете исключения, когда-либо).
«Ой, нельзя писать операторы преобразования между базовыми / производными типами». .Net позволяет это сделать, и это можно сделать с помощью C#. См. stackoverflow.com/a/15987692/1497385
Это не сработает. Перейдите на страницу справки, на которую указывает ошибка компиляции.
Лучшее решение - использовать здесь фабричные методы.
Лол ... Я знаю, что пытался передать свой мыслительный процесс ... поверьте мне, я пытался: D
«Это не работает :)» .Net позволяет это, и это возможно с помощью C#. См. stackoverflow.com/a/15987692/1497385
Это называется опровержением, и предложение Селдэка использовать «безопасную» версию разумно.
Вот довольно приличный описание с примерами кода.
Понижающее преобразование имеет смысл, если у вас есть объект производного класса, но на него ссылается ссылка типа базового класса, и по какой-то причине вы хотите, чтобы на него снова ссылалась ссылка на тип производного класса. Другими словами, вы можете понижать, чтобы отменить эффект предыдущего повышения. Но у вас не может быть объекта базового класса, на который ссылается ссылка типа производного класса.
Интересно, что когда вы «обращаете» восходящее преобразование, вся «лишняя» полученная информация снова становится доступной. Ясно, что он каким-то образом сохранился в куче памяти.
Я читал об этой проблеме. Причина, по которой информация сохраняется на этом объекте, заключается в том, что сам объект не изменяется при его повышении, а только ссылка на него. И RE этот ответ кажется вполне законным поводом для того, чтобы опускать руки. Особенно при работе с SOA.
Нет, это невозможно. На управляемом языке, таком как C#, это просто не сработает. Среда выполнения этого не допустит, даже если компилятор пропустит.
Вы сами сказали, что это глупо:
SomeBaseClass class = new SomeBaseClass();
SomeDerivedClass derClass = (SomeDerivedClass)class;
Итак, спросите себя, действительно ли class является экземпляром SomeDerivedClass? Нет, поэтому преобразование не имеет смысла. Если вам нужно преобразовать SomeBaseClass в SomeDerivedClass, вы должны предоставить какое-то преобразование, либо конструктор, либо метод преобразования.
Однако похоже, что ваша иерархия классов нуждается в некоторой доработке. В общем случае не должен возможно преобразовать экземпляр базового класса в экземпляр производного класса. Как правило, должны быть данные и / или функции, не относящиеся к базовому классу. Если функциональность производного класса применяется к экземплярам все базового класса, то ее следует либо свернуть в базовый класс, либо вывести в новый класс, который не является частью иерархии базового класса.
«Нет, это невозможно. На управляемом языке, таком как C#, это просто не сработает». .Net позволяет это сделать, и это можно сделать с помощью C#. См. stackoverflow.com/a/15987692/1497385
Это невозможно, потому что как вы собираетесь получить «лишнее», которое есть у производного класса. Как компилятор узнает, что вы имеете в виду производный класс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.
Вместо «Яблоко есть фрукты» вы могли бы прочитать это как «Яблоко обладает свойствами фрукта», что звучит намного лучше и на самом деле точнее, чем «Яблоко - это фрукт».
Я бы сказал, что «свойство быть плодом» не будет ни композицией, ни наследованием, а реализацией интерфейса :)
Как отмечали другие, предложенный вами кастинг на самом деле невозможен. Может быть, это тот случай, когда можно будет ввести Шаблон декоратора (извлечение Head First)?
.Net позволяет это сделать, и это можно сделать с помощью C#. См. stackoverflow.com/a/15987692/1497385
Задумывались ли вы об интерфейсе, который в настоящее время будет реализовывать ваш базовый и производный классы? Я не знаю, почему вы реализуете этот способ, но это может сработать.
Я не знаю, почему никто не сказал этого, и я, возможно, что-то пропустил, но вы можете использовать ключевое слово 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));
Лично я не думаю, что в этом случае стоит использовать наследование. Вместо этого просто передайте экземпляр базового класса в конструктор и обращайтесь к нему через переменную-член.
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
{
}
}
}
Я нашел это очень полезным, особенно при преобразовании списка базовых классов в другой список с большей функциональностью в каждом элементе списка.
Но в этом случае вызвать методы базового класса невозможно, так как в ExtendedClass их вообще нет.
Да, вы можете: extendedObject.BaseClass.BaseClassMethod (args)
Это похоже на паттерн "Декоратор", который в данном случае подойдет, и его стоит поискать в Google! У шаблонов дизайна есть большинство ответов !!
Язык 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>?
@BoppityBop будет работать так же, как List<long> = List<int> или List<int> = List<long> - не сработает. Однако вы можете создать свою собственную коллекцию с некоторыми операторами неявного преобразования.
просто быть чистым. Вы говорите, что op_Implicit в соответствии с вашим решением не будет называться, когда базовые / производные находятся в общей коллекции. Правильно?
@BoppityBop Да. Операторы статичны. Нет даже наследования операторов. Гораздо меньше «наследования оператора типа элемента универсального класса».
Как указывалось во многих ответах, вы не можете опускать руки, что имеет смысл.
Однако в вашем случае 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>();
Это действительно работает, только если все данные в базовом классе находятся в форме доступных для чтения и записи свойств.
В этом вопросе неправильно использовать термин «отливка». Приведение подразумевает, что отбрасываемый объект уже является объектом этого типа. Например: объект o = 3; MethodWithIntegerParam ((int) o); В вашем вопросе baseClass не создается с производным классом. Итак, вы ищете конверсию, а не кастинг.