Подтвердить что ты не робот

InvalidCastException: Невозможно передать объекты типа [base] для ввода [подкласс]

У меня есть пользовательский CustomMembershipUser, который наследуется от MembershipUser.

public class ConfigMembershipUser : MembershipUser
{
    // custom stuff
}

Я использую Linq-to-SQL для чтения из базы данных и получения объекта User; чтобы сделать эту функцию членом MemberhipUser Я определил явное преобразование:

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 оператор CustomMembershipUser (System.Web.Security.MembershipUser) ': пользовательские преобразования к базовому классу или из него не допускаются.

Итак... Я не могу отбрасывать из базового класса в подкласс?

4b9b3361

Ответ 1

У вас это в обратном порядке: приведение из объекта базового класса в подкласс всегда будет неудачным, потому что базовый класс имеет только свойства базового класса (а не подкласса).

Так как, как вы говорите, подкласс имеет все свойства базового класса (он "есть -объект базового класса" ), то приведение от подкласса к базовому классу всегда будет успешным, но никогда не обратное.

Другими словами, вы можете думать о всех леопардах как о кошках, но вы не можете взять произвольного кота и относиться к нему как к леопарду (если только он не будет начинать с леопарда).

Вам нужно либо вернуть объект CustomMembershipUser вместо объекта MembershipUser, либо определить другую функцию explicit cast, которая преобразует MembershipUsers в CustomMembershipUsers, создав новый объект CustomMembershipUser. Вы не можете получить объект CustomMembershipUser из ниоткуда; он создается сначала либо напрямую, либо путем создания подкласса CustomMembershipUser (а не базового класса).

Edit:

Я ошибся в определении явного приведения в подкласс. Это невозможно (как показывает ошибка). Теперь вы, похоже, находились в той же ситуации, что и вопрос этого вопроса. Кастинг на самом деле не такой путь - либо создайте объекты CustomMembershipUser для начала (которые можно использовать непосредственно как объекты MembershipUser), либо напишите метод преобразования, который принимает MembershipUser и создает CustomMembershipUser.

Единственный раз, когда имеет смысл бросать из базового объекта в объект подкласса, это когда он уже является объектом подкласса (но переменная, содержащая его, относится к типу базового класса).

Ответ 2

Переменная типа MemberhipUser может содержать объект типа CustomMembershipUser, потому что подтип является экземпляром супертипа. Но обратное неверно.

Пользователь CustomMembershipUser может иметь членов, которые не входят в MemberhipUser. Поэтому переменная типа CustomMembershipUser не может содержать объект типа MembershipUser. В противном случае код может попытаться получить доступ к одному из этих элементов, который он не содержит.

Это не удается:

CustomMembershipUser custUser = memUser; 

потому что вы можете следить за этим:

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

Сообщение "Явные литые существа"

Причина, по которой вы получаете сообщение "явное приведение", не потому, что вы создали листинг от User to MembershipUser. (Тип пользователя здесь вообще не задействован.) Это связано с тем, что явный приведение всегда существует из супертипа в подтип. Это часть дизайна языка. Это должно поддерживать scenerio, в котором вы знаете, что объект относится к подтипу, и вы хотите использовать переменную, которая соответствует. Но если вы используете это явное приведение объекта, который не относится к типу-мишенью, тогда вы получите ошибку времени выполнения (как вы уже убедились).

Дальнейшее объяснение причин отказа при выполнении

В С# каждый объект имеет тип. Этот тип никогда не может быть изменен на время жизни объекта. Как только вы создаете сотрудника (например), он всегда будет Работником навсегда и всегда или до сбора мусора, аминь.

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. Причина этого заключается в том, что система знает, что каждый член Лица, который он пытается использовать, будет доступен из-за наследования.

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

Теперь попробуем это другим способом.

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

Это вызывает ошибку. Нет никакого неявного перевода из Person to Employee, поскольку компилятор не может гарантировать, что переменная Person содержит объект Employee. Таким образом, возникает ошибка времени компиляции. Итак, попробуйте явное приведение.

Employee newHire = (Employee)johnny;

Это будет просто отлично. Это разрешено компилятором, потому что иногда переменная Person будет содержать объект Employee. Но это не удастся во время выполнения. Причина, по которой это произойдет, состоит в том, что переменная johnny не имеет сотрудника, поэтому ее нельзя рассматривать как одну. Таким образом генерируется недопустимое исключение броска.

Если это не вызвало недопустимое исключение литых, мы могли бы попытаться сделать что-то вроде этого:

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

Но свойство не существует, потому что объект действительно является Человеком, а не Работником.

Итак, вы можете видеть, что притязание implicit от подтипа к супертипу, потому что это всегда удается и не вызывает проблем. Существует явный приведение от супертипа к подтипу, потому что это работает только в том случае, если тип среды выполнения является присвоением, совместимым с переменной. Ожидается, что программист узнает, когда он работает, а когда нет, и только делает бросок, когда он будет работать. В противном случае среда выполнения обнаружит недопустимый листинг и выдаст исключение.

Теперь иногда пользователь может создать пользовательский оператор преобразования, который может использоваться для перевода из одного типа в другой. Когда это произойдет, создается совершенно новый объект целевого типа. Однако это невозможно сделать вверх или вниз по иерархии наследования, потому что приведения для них уже предоставляются компилятором С#. Чтобы сделать пользовательский оператор преобразования, исходный или целевой тип не должен быть предком или decendent другого типа.

Ответ 3

Возможно сделать бросок, однако вам нужно пропустить объект, самый простой способ сделать это - создать вызов сам по себе, который возвращает тот же объект, как описано здесь: Проблема с использованием прокси-сервера при использовании таблицы NHibernate для каждой стратегии наследования подкласса