C#: разрешение недопустимого исключения приведения между унаследованным классом и его базой

у меня есть два класса: Post и Question. Вопрос определяется как:

public class Question : Post
{
//...
}

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


чего я хочу добиться

у меня есть объект типа Post, члены которого заполнены. Теперь я хочу преобразовать его в вопрос, чтобы добавить значения для этих нескольких других членов.

Это мой текущий код, используя явное приведение преобразование:

Post postToQuestion = new Post();

//Populate the Post...

Question ques = (Question)postToQuestion; //--> this is the error!

//Fill the other parts of the Question.

Я получаю исключение InvalidCastException. Что я делаю не так?

5 ответов


проблема в том, что вы не можете разыграть от родителя к ребенку. Можно создать конструктор для дочернего класса, который принимает родительский класс в качестве параметра: Вопрос ques = новый вопрос (myPost);

вы также можете использовать неявный оператор, чтобы упростить это: Вопрос ques = myPost;

http://www.codeproject.com/KB/cs/Csharp_implicit_operator.aspx

изменить: На самом деле, я только что пытался напечатать демо для вас, делая неявный оператор:

class Question : Post
{
    public Question()
    {
        //...
    }

    public Question(Post p)
    {
        // copy stuff to 'this'
    }

    public static implicit operator Question(Post p)
    {
        Question q = new Question(p);
        return q;
    }
}

но c# не позволяет выполнять неявные преобразования с базовым классом.


сообщение не является вопросом на данный момент, и CLR справедливо жалуется. Вы можете поднять вопрос, чтобы опубликовать, но не наоборот. Теперь, если у вас была ссылка высокого уровня на пост, который вы знал был вопрос, вы могли бы опуститься так:

public Post Post 
{
    set
    {
        if (value is Question)
        {
            question = (Question)value;
        }
    }
}

но даже это запах код.

но я думаю, что то, чего вы пытаетесь достичь, может быть достигнуто без кастинга класса или, возможно, вообще без наследования. После маститых "предпочтение инкапсуляции над наследованием" принцип, почему бы просто не обернуть объект Post в объект Question чем-то вроде:

public class Question 
{
    Post post;
    public Question(Post post)
    {
        this.post = post;
    }
}

предполагая, что вы определили свойства для соответствующих членов Post, а не

Question ques = (Question)post;

ты

Question ques = new Question(post);

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

обратите внимание, что выполнение приведения другим способом, от "вопрос" до "сообщение", будет работать нормально, потому что вы можете рассматривать вопрос как сообщение без необходимости его изменения - все необходимые поля уже там.


если вы хотите изменить тип, вам нужно либо написать код, либо иногда вы можете сериализовать. Приведение не изменяет тип (если только не был написан пользовательский оператор).

на простой опция состоит в том, чтобы создать его как правильный тип в первую очередь:

Post post = new Question();

кроме того, добавить оператор преобразования или какой-либо другой метод для создания Question после:

class Question {
    public Question(Post post) {
        this.Author = post.Author;
        this.Body = post.Body;
        // ...
    }
}
...
Question question = new Question(post);

вы также можете использовать другие трюки, как копии свойств на основе отражения или сериализация; например, использование MiscUtil:

Question question = PropertyCopy<Question>.CopyFrom(post);

или protobuf-чистая (требуется несколько дополнительных атрибутов сериализации):

Question question = Serializer.ChangeType<Post,Question>(post);

Не могли бы вы сделать это:

public Question(Post P) : base(p) {...}

class Post 
{
    public Post(Post P)
    {
         populate(P.ID);
    }

    void populate(int ID)
    {
         // load from DB, or however it happens
    }
}

...Если вы создаете экземпляр Post из хранилища данных в любом случае?