Рейтинг:
6
F-ES Sitecore
Ваша трудность в понимании, вероятно, связана с вашим конкретным примером. У вас есть Subscription и ISubscription, и между ними существует взаимно однозначное отношение, и вы действительно можете использовать их как взаимозаменяемые. Если вы посмотрите на более общий пример интерфейсов, вы лучше поймете. Разница между подпиской и ISubscription заключается в том, что подписка может ссылаться только на конкретный класс Subcription, однако ISubscription может ссылаться на * любой * конкретный класс, реализующий ISubscription. В вашем примере есть только один такой класс, Subscription, поэтому сложнее понять разницу между интерфейсами и классами.
В приведенном ниже примере используется интерфейс IAnimal и два конкретных класса Cat и Dog, которые реализуют этот интерфейс.
static Cat GetCatObject()
{
return new Cat();
}
static IAnimal GetCatByInterface()
{
return new Cat();
}
static Dog GetDogObject()
{
return new Dog();
}
static IAnimal GetDogByInterface()
{
return new Dog();
}
static void Main(string[] args)
{
IAnimal catA = GetCatByInterface();
// catA is IAnimal so it can point to any object that implements IAnimal
// GetCatByInterface returns Cat so the code above is valid however catA
// can point to anything that is IAnimal so the line below is also valid
catA = GetDogByInterface();
// so something of type IAnimal can reference anything IAnimal
// now we'll use concrete classes rather than interfaces
Cat catC = GetCatObject();
// the code above is fairly obvious, catC is Cat and GetCatObject returns Cat
Cat catD = (Cat)GetCatByInterface();
// catD can only point to an object that is Cat, however GetCatByInterface can return
// anything that implements IAnimal, and that includes Dog, there is no guarantee it returns Cat.
// The calling code doesn't know what concrete object GetCatByInterface returns, only that it is IAnimal.
// So we have to cast whatever comes back to Cat. Now look at the line below
Cat catE = (Cat)GetDogByInterface();
// this code compiles because the calling code still doesn't know what GetDogByInterface
// returns. We know it returns IAnimal so it could return Cat, Dog, Chicken, however
// when the code runs it will throw an exception as the object that GetDogByInterface
// returns can't be cast to Cat.
Console.ReadLine();
}
Member 13462842
Cat catE = (Cat)GetDogByInterface();
//если я хочу кошку, зачем мне вызывать GetDogbyInterface ()?
когда я знаю, что он все равно выдаст исключение.
Спасибо!
F-ES Sitecore
Вы знаете, что он возвращает собаку из-за имени, которое я ему дал. Допустим, у меня есть удостоверение личности пациента, у которого есть собака, и мне нужен экземпляр собаки этого пациента.
Dog dog = (Dog)GetPatientPet(123);
теперь предположим, что я выполняю процедуру кошки для другого пациента
Кот = (кошка)GetPatientPet(456);
Такой код * опасен, поскольку мы знаем, что GetPatientPet может вернуть любое животное, и мы делаем предположение, что пациент 123 вернет собаку и кошку 456. На самом деле вы бы использовали такие вещи, как ключевое слово "as", а не кастинг
Cat cat = GetPatientPet(456) as Cat;
теперь, если у пациента 456 есть собака, а не кошка, то cat-это null, а не исключение.
Member 13462842
такой код опасен. вы имеете в виду избегать такого кода?
что бы вы предложили?
F-ES Sitecore
Это опасно, поскольку вы делаете предположения о том, что конкретный класс возвращается. Опасный бит-это приведение, поэтому, как было предложено, вы должны использовать "as" вместо этого, поскольку это возвращает null, если приведение недопустимо, а не создает исключение.
Рейтинг:
20
OriginalGriff
Предполагая, что подписка реализует ISubscription, существует явное приведение:
Subscription sub = (Subscription) iSub;
Но вы не можете сделать это неявно, как это:
Subscription sub = iSub;
А почему бы и нет? Что ж... это категорически запрещено!
Если вы посмотрите здесь: (это устаревшая документация, так что вам придется скачать PDF-файл)
Там написано::
Цитата:
Пользовательские преобразования не разрешаются для преобразования из интерфейсных типов или в интерфейсные типы. В частности, это ограничение гарантирует, что при преобразовании в тип интерфейса не произойдет никаких определяемых пользователем преобразований и что преобразование в тип интерфейса будет успешным только в том случае, если преобразуемый объект действительно реализует указанный тип интерфейса.
Это означает, что вы не можете определить неявное преобразование из интерфейса в конкретный тип.
Member 13462842
Не могли бы вы помочь угадать использование "cast iSub to Sub" ?
Поскольку подписка реализует iSub, почему бы просто не создать экземпляр подписки?
OriginalGriff
Потому что вы получите новый экземпляр.
Вы не можете создать экземпляр интерфейса: только конкретный класс, производный от него (имеет смысл, когда вы думаете об этом, точно так же, как вы не можете жить в договоре аренды, но вам нужен договор аренды, чтобы арендовать дом - дом конкретный, соглашение-это интерфейс).
Поэтому, когда вы используете переменную типа интерфейса, она всегда содержит экземпляр производного класса - но какой? Может быть, тысячи...
Надо идти, кот только что выпустил живую мышь в моей компьютерной комнате...
Member 13462842
извините, все еще в замешательстве. Предполагая, что подписка реализует ISubscription.
а) подписка sub = (подписка) iSub;
б)подпункт подписка = новые подписки();
является подпункт в) экземпляр подписки?
являются ли суб В А) и Б) разными?
Спасибо!
OriginalGriff
а) может быть. Если iSub содержит экземпляр подписки (или класс, производный от подписки), то да. Если он содержит несвязанный экземпляр класса, то вы получите ошибку времени выполнения.
public class Subscription : ISubscription {...}
public class NotASubscription : ISubscription {...}
...
ISubscription s = new Subscription();
Subscription sub = (Subscription) s; // Works.
s = new NotASubscription();
sub = (Subscription) s; // Compiles, but throws an InvalidCastException
А и в-это одно и то же? Нет. B создает новый экземпляр класса, который не имеет отношения к существующему. Подумайте об этом так:
У тебя есть машина. Это такой же экземпляр классного автомобиля, как и мой, но это не один и тот же экземпляр: ваш-красный "Феррари", мой-черный "Мерседес".
Но вы всегда называете автомобиль "мой автомобиль" - у вас есть переменная, которая" указывает", какой автомобиль принадлежит вам. Если вы положите свой телефон в бардачок своей машины:
myCar.GloveBox.Add(myPhone)
а потом пересесть на новый Ламборгини:
myCar = new Lamborghini();
Можно ли ожидать, что микар.Перчаточный ящик, чтобы все еще содержать ваш телефон?