Приведение в C # с интерфейсами, позволяющими использовать все функциональные возможности объекта

#c# #interface

#c# #интерфейс

Вопрос:

У меня возникла небольшая проблема с приведением моих объектов в C #, чтобы иметь возможность использовать дополнительные объектные методы, помимо тех, которые объявлены в интерфейсе. Ниже приведен простой пример того, о чем я говорю.

 public interface IShape
{
    void Print();
}

public class Square : IShape
{
    #region IShape Members

    public void Print()
    {
        HttpContext.Current.Response.Write("Square Print Called"); 
    }   

    #endregion

    public void PrintMore()
    {
        HttpContext.Current.Response.Write("Square Print More Called"); 
    }        
}
  

Почему при вызове этого кода ниже я не могу получить доступ к PrintMore()?

 IShape s = (Square)shape;
s.PrintMore() // This is not available. only Print() is. 
  

Любая помощь и объяснение были бы полезны?

Комментарии:

1. Потому что ваш объект является «формой», а не «квадратом». Это «проблема» приведения. Это абсолютно нормально 🙂

2. Ваш конечный регион находится не в том месте. Это немного сбивает с толку.

3. @CodeInChaos: Я не думаю, что это так. В интерфейсе есть только один метод, и он находится внутри региона. Неинтерфейсный метод находится за его пределами…

4. @Chris проверьте историю редактирования. Это было на момент комментария.

5. @CodeInChaos: Ах, мои извинения. Обычно я стараюсь быть хорошим в этом, но, очевидно, на этот раз пропустил это. 🙂

Ответ №1:

Ваша s переменная по-прежнему имеет тип IShape . То, что вы случайно использовали приведение при присвоении ему, не меняет тип с точки зрения компилятора. Вам понадобится:

 Square s = (Square) shape;
s.PrintMore();
  

Конечно, это сработает, только если shape действительно является a Square (при условии, что не выполняются пользовательские преобразования).

Однако я бы посоветовал вам тщательно подумать, прежде чем идти по этому пути. Обычно подобное приведение указывает на то, что вы несколько нарушаете абстракцию — если вы знаете только о shape качестве IShape , вы должны (обычно) быть в состоянии делать то, что вам нужно, только с членами IShape . Если это не так, вы можете:

  • Сделать IShape более мощным (предоставить ему больше элементов)
  • Измените свой код, чтобы он принимал Square вместо an IShape
  • Приведение при крайней необходимости

Комментарии:

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

Ответ №2:

Проблема в том, что вы пытаетесь получить доступ PrintMore() из IShape ссылки, IShape ссылки видят только Print() метод, который они объявляют.

Итак, приведение, которое у вас есть, (Square) shape ничего не делает, потому что оно немедленно сохраняет его в IShape ссылку. Вам нужно, чтобы сама ссылка была Square либо приведена в действие и сохранена в Square ссылке, либо приведена в действие перед вызовом:

 Square s = (Square) shape;
s.PrintMore();
  

Или

 IShape s = shape;
((Square)s).PrintMore();
  

Или, если вы не уверены, является ли это Square или нет, используйте as приведение:

 Square s = shape as Square;

// will cast if it is a Square, otherwise, returns null 
// this doesn't work for value types (int, struct, etc) 
if (s != null)
{
    s.PrintMore();
}
  

Ответ №3:

В строке IShape s = (Square)shape; вы сообщаете компилятору, что s является IShape, поэтому доступны только методы в IShape.

Если вы пользователь:

 Square s = (Square)shape;
s.PrintMore()
  

Тогда это должно делать то, что вы хотите.

Ключевым моментом здесь является то, что когда вы объявляете переменную, вы сообщаете компилятору, что это такое, и тогда не имеет значения, какой объект вы в нее вставляете.

Ответ №4:

 IShape s = (Square)shape; 
s.PrintMore();
  

С этим кодом s все еще является IShape , а не Square . Переменная сохраняет тип, который вы определили при ее объявлении, что бы вы ни пытались в нее ввести.

Для работы код должен быть :

 Square s = (Square)shape; 
s.PrintMore();
  

Комментарии:

1. На самом деле это не отвечает на его вопрос — почему?

2. @JoelEtherton : добавлены некоторые пояснения.

Ответ №5:

Причина, по которой вы не можете получить доступ к PrintMore методу, заключается в том, что вы переназначаете приведенную к типу форму обратно переменной, определенной как IShape . Чтобы иметь возможность использовать методы в Square классе, вам нужно сохранить его в переменной типа Square , например:

 Square s = (Square)shape;
s.PrintMore(); 
  

или альтернативно:

 ((Square) shape).PrintMore();
  

Хотя, возможно, стоило бы хорошенько взглянуть на ваш код, приведения типов, подобные этим, обычно являются хорошим предупреждающим знаком о том, что, возможно, он не идеален. Возможно, IShape следует использовать PrintMore метод, или, возможно, вам следует принимать только Square объекты на этом этапе? По крайней мере, я бы посоветовал убедиться, что shape действительно имеет тип Square , прежде чем выполнять приведение этого типа.

Например:

 Square s = shape as Square;

if (s != null)
    s.PrintMore(); 
  

Ответ №6:

Потому что, когда вы пытаетесь s.PrintMore() , «s» имеет тип IShape, поэтому он знает только о функциях интерфейса. Вам нужно было бы сделать что-то вроде

 Square s = (Square)shape;  
s.PrintMore();
  

или

 ((Square)shape).PrintMore(); // assuming you're positive its a Square type
  

Представьте интерфейс как оболочку над вашим объектом, которая предоставляет только функции, определенные в интерфейсе. Они все еще существуют, вы просто не можете получить к ним доступ без приведения к соответствующему объекту.