C#: 解决继承类和其基类之间的无效转换异常

12

我有两个类,名为Post和Question。Question的定义如下:

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

我的Question类没有覆盖Post的任何成员,只是表达了一些其他成员。


我想实现的目标

我有一个类型为Post的对象,其成员已填充。现在,我想将其转换为Question,以便我可以为那几个其他成员添加值。

这是我的当前代码,使用显式转换:

Post postToQuestion = new Post();

//Populate the Post...

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

//Fill the other parts of the Question.

问题

我遇到了一个 InvalidCastException 异常。我做错了什么?

5个回答

23
问题在于无法将父类转换为子类。您可以为子类创建一个构造函数,该构造函数以父类作为参数: Question ques = new Question(myPost);
您还可以使用隐式操作符使其更简单: Question 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#不允许你使用基类进行隐式转换。


我刚刚尝试使用您的隐式操作符示例,但是我收到了编译器错误CS0553:“不允许将用户定义的转换应用于基类的值”。 - Maxim Zaslavsky
是的,我发帖后就注意到了。在我的帖子底部,有一篇文章的链接,讲解了你遇到的同样问题,并展示了他如何解决的例子。 - Joel

6

目前,Post不是一个问题,CLR(公共语言运行时)正在正确地抱怨。您可以将Question向上转换为Post,但不能反过来。现在,如果您有一个对Post的高级引用,并且您知道它是一个Question,您可以像这样向下转换:

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

假设您已经为帖子的相关成员定义了属性,而不是
Question ques = (Question)post;

你将会拥有

Question ques = new Question(post);

这加上接口继承将使其成为最直接的实现。 - GrayWizardx
1
-1 使用Java风格的setBlah是不好的,这就是.NET中属性的作用。 - RCIX
另外,应该使用“is”而不是“instanceof”。 - RCIX
抱歉,我脑海中一直在想Java,并且NetBeans很方便——但原则是相同的。我已经修复了C#。 - Dave Sims

4
进行类型转换并不会改变您正在转换的对象-因此它无法向您的“帖子”对象添加缺失的“问题”成员。很抱歉,您只需要创建一个新的“问题”对象,并将您感兴趣的成员从“帖子”复制到“问题”中。
请注意,反过来进行转换,从“问题”到“帖子”,将可以正常工作,因为您可以将“问题”视为“帖子”而无需进行更改-所有必需的字段已经存在。

3
如果您想要更改类型,您需要编写代码,有时可以序列化。强制转换不会更改类型(除非已编写自定义运算符)。最简单的选项是一开始就创建正确的类型:
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-net(需要一些额外的序列化属性):
Question question = Serializer.ChangeType<Post,Question>(post);

0

你能做到这个吗:

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,该怎么办?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接