InvalidCastException:无法将[base]类型的对象转换为[subclass]类型

时间:2011-03-09 00:44:12

标签: c# casting membershipuser

我有一个继承自MembershipUser的自定义CustomMembershipUser。

public class ConfigMembershipUser : MembershipUser
{
    // custom stuff
}

我正在使用Linq-to-SQL从数据库中读取并获取User实体;要将此功能作为MembershipUser,我已经定义了一个显式转换:

public static explicit operator MembershipUser(User user)
{
    DateTime now = DateTime.Now;

    if (user == null) return null;

    return new MembershipUser("MagicMembershipProvider", 
                              user.DisplayName, user.Id, 
                              user.Email, "", "", true, false, 
                              now, now, now, now, now);
}

此演员表工作正常:

MembershipUser memUser = (MembershipUser) entityUser;

但是,对CustomMembershipUser的第二次转换失败:

MembershipUser memUser = (MembershipUser) entityUser;
CustomMembershipUser custUser = (CustomMembershipUser) memUser;

如果我将演员阵容改为

CustomMembershipUser custUser = memUser;

我收到一个intellisense错误告诉我隐式转换无法正常运行但存在显式转换

...并且最重要的是,我无法明确定义从基类到子类的强制转换。我尝试过但失败了。我最不了解的是为什么从基类转换为子类永远失败?根据定义,子类具有基类的所有属性,所以问题是什么。

修改

我尝试定义从MembershipUser到CustomMembershipUser的显式转换(首先我为转换定义了一个私有构造函数):

private ConfigMembershipUser(MembershipUser user)
    : base(user.ProviderName, user.UserName, user.ProviderUserKey, user.Email,
           user.PasswordQuestion, user.Comment, user.IsApproved, user.IsLockedOut,
           user.CreationDate, user.LastLoginDate, user.LastActivityDate, 
           user.LastPasswordChangedDate, user.LastLockoutDate)
    {
        // initialize extended CustomMembershipUser stuff here
    }

然后我定义了我的自定义演员:

public static explicit operator CustomMembershipUser(MembershipUser user)
{
     return new CustomMembershipUser(user);
}

我收到以下错误:

  

'CustomMembershipUser.explicit operator CustomMembershipUser   (System.Web.Security.MembershipUser)':用户定义的转换   不允许进出基类

所以......我不能从基类转换为子类?

3 个答案:

答案 0 :(得分:30)

你反过来了:从基类的对象到子类的强制转换总是失败,因为基类只有 的属性基类(不是子类)。

因为,正如你所说,子类具有基类的所有属性(它是“is-a”基类对象),然后从子类到基类的转换将始终成功,但从不反过来

换句话说,你可以把所有的豹都想象成猫,但是你不能把它当作豹子来对待它(除非它已经是豹子了)。

您需要返回一个CustomMembershipUser对象而不是MembershipUser个对象,或者定义另一个显式强制转换单独函数,该函数通过创建一个新的{{{{{来将MembershipUsers转换为CustomMembershipUsers 1}}对象。你不能无处获得CustomMembershipUser对象;首先要创建它,要么直接创建,要么实例化CustomMembershipUser的子类(不是基类)。

编辑:

我错误地定义了对子类的显式强制转换。这是不可能的(因为您看到的错误表明)。你现在似乎与this question的提问者情况完全相同。转换不是真正的方法 - 要么创建CustomMembershipUser对象开始(可直接用作CustomMembershipUser个对象),要么编写一个接受MembershipUser和创建MembershipUser

从基础对象转换为子类对象的唯一时间是已经一个子类对象(但保存它的变量属于基类类型)。

答案 1 :(得分:10)

MembershipUser类型的变量可以包含CustomMembershipUser类型的对象,因为子类型是超类型的实例。但反过来却不正确。

CustomMembershipUser可能包含不在MembershipUser上的成员。因此,CustomMembershipUser类型的变量不能保存MembershipUser类型的对象。否则,代码可能会尝试访问其中不包含的成员之一。

这失败了:

CustomMembershipUser custUser = memUser; 

因为你可以用它来跟进:

custUser.CustomStuff();   // Oops! Can't call CustomStuff() on a MembershipUser object!

“明确演员阵营”消息

您获得“显式转换存在”消息的原因并不是因为您已经创建了从User到MembershipUser的转换。 (这里根本不涉及用户类型。)这是因为从超类型到子类型存在显式的强制转换总是。这是语言设计的一部分。这是为了支持您知道对象属于子类型并且您想要使用匹配的变量的场景。但是,如果您对不属于目标类型的对象使用该显式强制转换,则会出现运行时错误(如您所见)。

进一步解释演员阵容失败的原因

在C#中,每个对象都有一个类型。该类型永远不会在对象的生命周期中更改。一旦你创建了一个Employee(例如),它将永远是一个Employee永远,或者直到垃圾收集,amen。

public class Person
{
    public string Name {get; private set;}
    public Person(string name)
    {  Name = name; }
}
public class Employee : Person
{
    public DateTime HireDate {get; private set;}
    public Employee(string name, DateTime hireDate)
        : base (name)
    {    HireDate = hireDate;  }
}

如果你有一个Person类型的变量,那么该变量可以包含一个Employee对象,因为Employee是一个Person。

Employee mike = new Employee("Michael", DateTime.Now);
Person myBestBud = mike;

这是一个非常明显的演员阵容,因为它始终有效。 Person变量可以始终持有Employee对象。原因是因为系统知道它试图使用的每个Person成员都会因为继承而可用。

Console.WriteLine("Dude's name: " + myBestBud.Name);

现在,让我们以另一种方式尝试。

Person johnny = new Person("Johnny Johnson");
Employee newHire = johnny;  // ERROR - Attempt to assign...etc.  An explicit cast is available...

这会导致错误。从Person到Employee没有隐式转换,因为编译器无法保证Person变量包含Employee对象。这会导致编译时错误。所以,让我们尝试一下显式演员。

Employee newHire = (Employee)johnny;

这将编译得很好。这是编译器允许的,因为有时Person变量将保存Employee对象。但这会在运行时失败。这将失败的原因是因为变量johnny没有雇员,因此不能将其视为一个雇员。因此抛出了无效的强制转换异常。

如果它没有抛出无效的强制转换异常,那么我们可以尝试这样做:

Console.WriteLine("Hired on: " + newHire.HireDate);

但该属性不存在,因为该对象实际上是Person,而不是Employee。

所以你可以看到从子类型到超类型的隐含转换,因为它总是成功并且不会导致任何问题。存在从超类型到子类型的显式转换,因为只有在对象的运行时类型与变量赋值兼容时才有效。程序员应该知道它何时起作用,什么时候不起作用,并且只有当它起作用时才进行演员表演。否则,运行时将检测到无效的强制转换并抛出异常。

现在,用户有时可以创建一个自定义转换运算符,可用于从一种类型转换为另一种类型。当发生这种情况时,会创建一个目标类型的全新对象。但是,这不能在继承层次结构中向上或向下完成,因为这些转换已经由C#编译器提供。为了执行自定义转换运算符,源类型或目标类型不能是其他类型的祖先或后代。

答案 2 :(得分:0)

可以进行转换,但是你需要对代理进行解代,最简单的方法是创建一个返回同一对象的自身调用,如下所述:Issue with Casting proxies when using NHibernate table per subclass inheritance strategy