InvalidCastException: Unable To Cast Objects of type [base] to type [subclass]

Scott Baker picture Scott Baker · Mar 9, 2011 · Viewed 87.5k times · Source

I have a custom CustomMembershipUser that inherits from MembershipUser.

public class ConfigMembershipUser : MembershipUser
{
    // custom stuff
}

I am using Linq-to-SQL to read from a database and get a User entity; to make this function as a MembershipUser I have defined an explicit conversion:

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

This cast works fine:

MembershipUser memUser = (MembershipUser) entityUser;

However, the second cast to CustomMembershipUser fails:

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

If I change the cast to

CustomMembershipUser custUser = memUser;

I get an intellisense error telling me an implicit cast won't work but an explicit cast exists.

... and to top it all off, I can't apparently define a cast from a base class to a subclass. I tried it and it failed. The thing I don't understand most of all is why would a cast from a base class to a subclass ever fail? By definition the subclass has all of the properties of the base class, so what's the problem.

EDIT

I tried to define an explicit cast from MembershipUser to CustomMembershipUser (first I defined a private constructor for the cast):

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
    }

Then I defined my custom cast:

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

and I got the following error:

'CustomMembershipUser.explicit operator CustomMembershipUser (System.Web.Security.MembershipUser)': user-defined conversions to or from a base class are not allowed.

So... I can't cast from a base class to a subclass?

Answer

Cameron picture Cameron · Mar 9, 2011

You've got it in reverse: A cast from an object of a base class to a subclass will always fail, because the base class has only the properties of the the base class (not the subclass).

Since, as you say, the subclass has all the properties of the base class (it "is-a" base-class object), then a cast from the subclass to the base class will always succeed, but never the reverse.

In other words, you can think of all leopards as cats, but you cannot take an arbitrary cat and treat it like a leopard (unless it's already a leopard to begin with).

You need to either return a CustomMembershipUser object instead of a MembershipUser object, or define another explicit cast separate function which converts MembershipUsers to CustomMembershipUsers by making a new CustomMembershipUser object. You cannot get a CustomMembershipUser object out of nowhere; it has be created first, either directly or by instantiating a subclass of CustomMembershipUser (not a base-class).

Edit:

I was wrong about defining an explicit cast to the subclass. This is not possible (as the error you see indicates). You now seem to be in the exact same situation as the asker of this question. Casting is not really the way to go here -- either create CustomMembershipUser objects to begin with (which are usable directly as MembershipUser objects), or write a conversion method which accepts a MembershipUser and creates a CustomMembershipUser.

The only time it makes sense to cast from a base object to a subclass object is when it is already a subclass object (but the variable holding it is of the base class type).