views:

1013

answers:

5

I have two classes, named Post and Question. Question is defined as:

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

My Question class does not override any members of Post, it just expresses a few other ones.


What I want to accomplish

I have an object of type Post, whose members are populated. Now, I want to convert it into a Question, so that I can add values for those few other members.

This is my current code, using an explicit cast conversion:

Post postToQuestion = new Post();

//Populate the Post...

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

//Fill the other parts of the Question.


Problem

I am getting an InvalidCastException. What am I doing wrong?

+4  A: 

Doing a cast doesn't change the object you are casting - so it can't add the missing 'Question' members to your 'Post' object. I'm afraid you're just going to have to create a new 'Question' object, and copy the members you're interested in from 'Post' to 'Question'.

Note that doing the cast the other way, from 'Question' to 'Post', would work fine, because you can treat a Question like a Post without having to change it - all the required fields are already there.

Bruce
+10  A: 

The problem is that you can't cast from parent to child. You can create a constructor for the child class that takes the parent class as a param: Question ques = new Question(myPost);

You could also use the implicit operator to make it simple to do this: Question ques = myPost;

http://www.codeproject.com/KB/cs/Csharp%5Fimplicit%5Foperator.aspx

EDIT: Actually, I just tried typing up a demo for you doing the implicit operator:

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

but aparently C# doesn't let you do implicit conversions with a base class.

This article here explains your problem pretty well.

Joel
I just tried using your implicit operator example, but I get compiler error CS0553: 'User-defined conversions to values of a base class are not allowed'.
Maxim Zaslavsky
Yeah, I noticed that right after I posted it. At the bottom of my post, there's a link to an article that explains the same problem you have, and shows an example of how he solved it.
Joel
+4  A: 

Post is not a Question at this point and the CLR is rightly complaining. You could upcast Question to Post but not visa-versa. Now, if you had a high-level reference to a Post that you knew was a Question, you could downcast like this:

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

But even this is a code smell.

But I think what you're trying to accomplish can be achieved without class casting, or maybe without inheritance at all. Following the venerable "favor encapsulation over inheritance" principle, why not just wrap the Post object into your Question object with something like:

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

Assuming you've defined properties for Post's relevant members, instead of

Question ques = (Question)post;

you'd have

Question ques = new Question(post);
Dave Sims
This plus interface inheritance would make this the most straightforward implementation.
GrayWizardx
-1 for using java style setBlah, this is what properties are for in .net
RCIX
also, it's "is", not "instanceof"
RCIX
sorry, had Java on the brain and NetBeans close to hand -- the principles are identical. I've fixed for C#.
Dave Sims
+1 now, much better
RCIX
+2  A: 

If you want to change the type, you need to either write code, or sometimes you can serialize. Casting doesn't change the type (unless a custom operator has been written).

The simplest option is to create it as the right type in the first place:

Post post = new Question();

Alternatively, add a conversion operator or some other method for creating a Question from a post:

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

You can also use other tricks, like reflection-based property copies, or serialization; for example, using MiscUtil:

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

or protobuf-net (requires some extra serialization attributes):

Question question = Serializer.ChangeType<Post,Question>(post);
Marc Gravell
A: 

Could you do this:

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

...If you instantiate Post from a datastore anyway?

Steve Syfuhs