30

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

public class ConfigMembershipUser : MembershipUser
{
    // custom stuff
}

我正在使用 Linq-to-SQL 从数据库中读取并获取用户实体;为了使这个函数成为 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;

我收到一个智能感知错误,告诉我隐式转换不起作用,但存在显式转换

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

编辑

我尝试定义从 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)':不允许用户定义的与基类之间的转换

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

4

3 回答 3

39

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

正如您所说,由于子类具有基类的所有属性(它“是”基类对象),因此从子类到基类的强制转换总是会成功,但绝不会反过来。

换句话说,您可以将所有豹子都视为猫,但您不能随便拿一只猫并将其视为豹子(除非它一开始就已经是豹子)。

您需要返回一个CustomMembershipUser对象而不是一个MembershipUser对象,或者定义另一个显式强制转换函数,通过创建一个新CustomMembershipUser对象将 MembershipUsers 转换为 CustomMembershipUsers。您无法从任何地方获取 CustomMembershipUser 对象;它是首先创建的,可以直接创建,也可以通过实例化CustomMembershipUser(不是基类)的子类来创建。

编辑:

我错误地定义了对子类的显式强制转换。这是不可能的(正如您看到的错误所示)。你现在似乎和这个问题的提问者处于完全相同的境地。强制转换并不是真正的方法——要么创建CustomMembershipUser对象开始(可直接用作MembershipUser对象),要么编写一个接受 aMembershipUser并创建 a的转换方法CustomMembershipUser

只有当它已经是一个子类对象(但持有它的变量是基类类型)时,才有意义将基对象转换为子类对象。

于 2011-03-09T00:50:54.580 回答
14

MembershipUser 类型的变量可以保存 CustomMembershipUser 类型的对象,因为子类型是超类型的实例。但反之则不然。

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

这失败了:

CustomMembershipUser custUser = memUser; 

因为你可以跟进它:

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

“存在显式演员表”消息

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

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

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

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# 编译器已经提供了这些转换。为了执行自定义转换运算符,源或目标类型不能是其他类型的祖先或后代。

于 2011-03-09T00:54:35.117 回答
0

可以进行强制转换,但是您需要取消代理对象,最简单的方法是创建对自身的调用以返回相同的对象,如下所述:使用每个子类使用 NHibernate 表时的强制转换代理问题继承策略

于 2012-05-23T16:29:38.047 回答