Способ приведения базового типа к производному типу - PullRequest
43 голосов
/ 24 сентября 2008

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

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

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

Ответы [ 17 ]

30 голосов
/ 24 сентября 2008

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

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

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

17 голосов
/ 24 сентября 2008

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

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

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

15 голосов
/ 26 сентября 2012

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

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
        {
        }
    }
}
9 голосов
/ 24 сентября 2008

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

7 голосов
/ 02 мая 2012

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

SomeDerivedClass layer = JsonConvert.DeserializeObject<SomeDerivedClass>(JsonConvert.SerializeObject(BaseClassObject));
5 голосов
/ 24 сентября 2008

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

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

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

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

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

5 голосов
/ 13 апреля 2013

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

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

[System.Runtime.CompilerServices.SpecialName]
public static Derived op_Explicit(Base a) { ... }
1 голос
/ 24 сентября 2008

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

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

1 голос
/ 19 октября 2009

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

1 голос
/ 12 января 2009

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

...