Member 13462842 Ответов: 2

Приведение интерфейса к классу


я читаю исходный код OPC-клиента.
какой-то синтаксис, которого я не понимаю.

Opc.Da.Subscription group;

          var item = server.CreateSubscription(groupState);   //item is a Interface
          group = (Subscription)item;


синтаксис такой: object= (class)Interface
означает ли это приведение интерфейса (элемента) к классу?

Что я уже пробовал:

группа = Элемент;

он возвращает ошибку:
Цитата:
CS0266 не удается неявно преобразовать тип 'ОРС.Да.ISubscription в ОРС.Да.Подписка'. Существует явное преобразование (вы пропускаете приведение?)


Вот почему я думаю, что это означает приведение интерфейса к классу.
но что это значит на самом деле?
спасибо

2 Ответов

Рейтинг:
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();

Можно ли ожидать, что микар.Перчаточный ящик, чтобы все еще содержать ваш телефон?

Richard Deeming

На новом сайте "docs" есть обновленная ссылка на спецификацию языка C# :
Спецификация чернового языка C# 6.0[^]

Я не могу найти точный раздел, который вы процитировали, но тема "конверсии", похоже, охватывает его:

Конверсии | пользовательские конверсии[^]
Классы / операторы преобразования[^]

Класс или структура могут объявлять преобразование из исходного типа S в целевой тип T только в том случае, если все следующие значения истинны:
...
Ни S, ни T не являются interface_type.