Наследование и переопределение методов - где ошибка?
От: J-Pro Молдова  
Дата: 27.05.09 14:22
Оценка: 1 (1)
Ребят, всем привет.

Так сложилось, что я сначала писал на С++, затем несколько лет на Java, теперь, вот, не оставляя Java, необходимо писать на C#. Возможно это поможет определить причины возникновения описанной ситуации.

Вопрос в следующем: имеется интерфейсы IThingManager и IThing. Первый — описание некоего управляющего вещью, второе — описание вещи:
public interface IThingManager
{
  IThing getAll();
  void add(IThing thing);
}

public interface IThing
{
  String name;
}


Ещё есть класс Thing, имплементирующий IThing:
public class Thing : IThing
{
}


Далее, имеется интерфейс ICarManager, наследующий IThingManager, а так же класс Car, наследующий Thing:
public interface ICarManager : IThingManager
{
  Car getAll();
  void add(IThing thing);
}

public class Car : Thing
{
  String color;
  int cost;
}


И последнее: есть класс CarManager, имплементирующий ICarManager:
public class CarManager : ICarManager
{
  Car getAll() {};
  void add(IThing thing) {};
}


Итак, имея всё вышеописанное, компилятор сообщает мне о том, что в интерфейсе ICarManager методом "Car getAll()" я скрываю(я так понял, что переопределяю) метод интерфейса IThingManager "IThing getAll()" и предлагает мне в случае намеренности этого поставить "new" в интерфейсе IThingManager. После того, как я это делаю, компилятор ругается, что класс CarManager не реализует необходимый метод интерфейса IThingManager "IThing getAll()".

Вопрос: как такое может быть, если один и тот же компилятор говорит мне, что я СКРЫВАЮ этот метод, а потом я его НЕ реализовываю? Противоречие?
В Java такой код ошибок не вызывает, поэтому я решил повторить его и в C#, но безуспешно, почему?

Заранее спасибо!
-= J-Pro =- with respect
Re: Наследование и переопределение методов - где ошибка?
От: OrSol  
Дата: 27.05.09 14:34
Оценка:
Здравствуйте, J-Pro, Вы писали:

JP>Вопрос: как такое может быть, если один и тот же компилятор говорит мне, что я СКРЫВАЮ этот метод, а потом я его НЕ реализовываю? Противоречие?

JP>В Java такой код ошибок не вызывает, поэтому я решил повторить его и в C#, но безуспешно, почему?

Смотрите в спецификации 13.4.1 Explicit interface member implementations
Re: Наследование и переопределение методов - где ошибка?
От: _FRED_ Черногория
Дата: 27.05.09 14:38
Оценка:
Здравствуйте, J-Pro, Вы писали:

JP>Далее, имеется интерфейс ICarManager, наследующий IThingManager, а так же класс Car, наследующий Thing:

JP>public interface ICarManager : IThingManager
JP>{
JP>  Car getAll();
JP>  void add(IThing thing);
JP>}


Зачем метод add принимает IThing? Точно такой же метод есть в базовом интерфейсе.
Help will always be given at Hogwarts to those who ask for it.
Re: Наследование и переопределение методов - где ошибка?
От: NovaCxarmulo Россия http://timofey.koolin.ru
Дата: 27.05.09 14:38
Оценка:
Здравствуйте, J-Pro, Вы писали:

JP>
JP>public interface IThingManager
JP>{
JP>  IThing getAll();
JP>  void add(IThing thing);
JP>}

JP>Далее, имеется интерфейс ICarManager, наследующий IThingManager, а так же класс Car, наследующий Thing:
JP>[c#]
JP>public interface ICarManager : IThingManager
JP>{
JP>  Car getAll();
JP>  void add(IThing thing);
JP>}

JP>И последнее: есть класс CarManager, имплементирующий ICarManager:
JP>[c#]
JP>public class CarManager : ICarManager
JP>{
JP>  Car getAll() {};
JP>  void add(IThing thing) {};
JP>}
JP>


JP>Итак, имея всё вышеописанное, компилятор сообщает мне о том, что в интерфейсе ICarManager методом "Car getAll()" я скрываю(я так понял, что переопределяю) метод интерфейса IThingManager "IThing getAll()" и предлагает мне в случае намеренности этого поставить "new" в интерфейсе IThingManager. После того, как я это делаю, компилятор ругается, что класс CarManager не реализует необходимый метод интерфейса IThingManager "IThing getAll()".


JP>Вопрос: как такое может быть, если один и тот же компилятор говорит мне, что я СКРЫВАЮ этот метод, а потом я его НЕ реализовываю? Противоречие?

JP>В Java такой код ошибок не вызывает, поэтому я решил повторить его и в C#, но безуспешно, почему?


в IThingManager должен быть определен метод IThing getAll();
в ICarManager Car getAll()

т.е. два метода с одинаковым названием, но разными возвращаемыми типами. Тут нужно реализовать оба метода и указать в одном из них явную принадлежность к интерфейсу, например так:

public class CarManager : ICarManager
{
  Car getAll(){return null;}
  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который определен без принадлежности к интерфейсам*/}
  ...
}
Сражение выигрывает тот, кто твердо решил его выиграть
(с) Л.Н. Толстой
Re: Наследование и переопределение методов - где ошибка?
От: Пельмешко Россия blog
Дата: 27.05.09 14:41
Оценка: 19 (1)
Здравствуйте, J-Pro, Вы писали:
JP>Итак, имея всё вышеописанное, компилятор сообщает мне о том, что в интерфейсе ICarManager методом "Car getAll()" я скрываю(я так понял, что переопределяю) метод интерфейса IThingManager "IThing getAll()" и предлагает мне в случае намеренности этого поставить "new" в интерфейсе IThingManager. После того, как я это делаю, компилятор ругается, что класс CarManager не реализует необходимый метод интерфейса IThingManager "IThing getAll()".

Сигнатура методов не включает в себя возвращаемое значение, поэтому действительно у Вас имена совпадают, нужен new:
public interface ICarManager : IThingManager
{
    new Car GetAll();
    new void Add(IThing thing);
}


Теперь ICarManager включает 4 метода, которые необходимо заимплементить, например так:
public class CarManager : ICarManager
{
    public Car GetAll() { ... }
    public void Add(IThing thing) { ... }

    IThing IThingManager.GetAll() { ... }
    void IThingManager.Add(IThing thing) { ... }
}


Члены ICarManager реализованы неявно, а члены "унаследованного" IThingManager — явно (explicit interface implementation)...
Re[2]: Наследование и переопределение методов - где ошибка?
От: NovaCxarmulo Россия http://timofey.koolin.ru
Дата: 27.05.09 14:47
Оценка:
NC>
NC>public class CarManager : ICarManager
NC>{
NC>  Car getAll(){return null;}
NC>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который определен без принадлежности к интерфейсам*/}
NC>  ...
NC>}
NC>


public class CarManager : ICarManager
{
  public Car getAll(){return null;}
  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который еделен без принадлежности к интерфейсам*/}
  ...
}
Сражение выигрывает тот, кто твердо решил его выиграть
(с) Л.Н. Толстой
Re[2]: Наследование и переопределение методов - где ошибка?
От: J-Pro Молдова  
Дата: 27.05.09 15:10
Оценка:
Здравствуйте, _FRED_, Вы писали:

_FR>Здравствуйте, J-Pro, Вы писали:


JP>>Далее, имеется интерфейс ICarManager, наследующий IThingManager, а так же класс Car, наследующий Thing:

_FR>
JP>>public interface ICarManager : IThingManager
JP>>{
JP>>  Car getAll();
JP>>  void add(IThing thing);
JP>>}
_FR>


_FR>Зачем метод add принимает IThing? Точно такой же метод есть в базовом интерфейсе.


Это моя ошибка, этого метода там быть не должно, прошу прощения. Но вопроса это не отменяет
-= J-Pro =- with respect
Re[3]: Наследование и переопределение методов - где ошибка?
От: J-Pro Молдова  
Дата: 27.05.09 15:20
Оценка:
Здравствуйте, NovaCxarmulo, Вы писали:

NC>>
NC>>public class CarManager : ICarManager
NC>>{
NC>>  Car getAll(){return null;}
NC>>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который определен без принадлежности к интерфейсам*/}
NC>>  ...
NC>>}
NC>>


NC>
NC>public class CarManager : ICarManager
NC>{
NC>  public Car getAll(){return null;}
NC>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который еделен без принадлежности к интерфейсам*/}
NC>  ...
NC>}
NC>


Спасибо за ответ. Это действительно решение.

Честно говоря, думал, что С# сам понимает, что у меня IThing — базовый инерфейс для Car и не попросит реализовывать метод IThing getAll()... Понимаете, о чём я? Т.е. это не просто ДРУГОЙ возвращаемый тип. Это, фактически, тот же тип, раз он унаследован от IThing....
-= J-Pro =- with respect
Re[3]: Наследование и переопределение методов - где ошибка?
От: J-Pro Молдова  
Дата: 27.05.09 15:49
Оценка:
Здравствуйте, NovaCxarmulo, Вы писали:

NC>>
NC>>public class CarManager : ICarManager
NC>>{
NC>>  Car getAll(){return null;}
NC>>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который определен без принадлежности к интерфейсам*/}
NC>>  ...
NC>>}
NC>>


NC>
NC>public class CarManager : ICarManager
NC>{
NC>  public Car getAll(){return null;}
NC>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который еделен без принадлежности к интерфейсам*/}
NC>  ...
NC>}
NC>


Только что попробовал. Результат: "Cannot implicitly convert type 'System.Collections.Generic.List<Car>' to 'System.Collections.Generic.List<IThing>'... Мне нужен именно список...
-= J-Pro =- with respect
Re[4]: Наследование и переопределение методов - где ошибка?
От: NovaCxarmulo Россия http://timofey.koolin.ru
Дата: 27.05.09 18:27
Оценка:
Здравствуйте, J-Pro, Вы писали:

JP>Здравствуйте, NovaCxarmulo, Вы писали:


NC>>>
NC>>>public class CarManager : ICarManager
NC>>>{
NC>>>  Car getAll(){return null;}
NC>>>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который определен без принадлежности к интерфейсам*/}
NC>>>  ...
NC>>>}
NC>>>


NC>>
NC>>public class CarManager : ICarManager
NC>>{
NC>>  public Car getAll(){return null;}
NC>>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который еделен без принадлежности к интерфейсам*/}
NC>>  ...
NC>>}
NC>>


JP>Только что попробовал. Результат: "Cannot implicitly convert type 'System.Collections.Generic.List<Car>' to 'System.Collections.Generic.List<IThing>'... Мне нужен именно список...


Угу, мне тоже не нравится что возврат унаследованных типов не принимается как реализация интерфейса, хотя непонятно почему.

Список нужно возвращать того типа, который требуется, т.е.
List<Car> cars = getAll();
List<IThing> res = new List<IThing>(car.Length);
for(int i = 0; i < cars.Length; ++i)
  res[i] = cars[i];


или что-то подобное.
Сражение выигрывает тот, кто твердо решил его выиграть
(с) Л.Н. Толстой
Re[4]: Наследование и переопределение методов - где ошибка?
От: Аноним  
Дата: 28.05.09 08:09
Оценка:
Здравствуйте, J-Pro, Вы писали:

JP>Здравствуйте, NovaCxarmulo, Вы писали:


NC>>>
NC>>>public class CarManager : ICarManager
NC>>>{
NC>>>  Car getAll(){return null;}
NC>>>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который определен без принадлежности к интерфейсам*/}
NC>>>  ...
NC>>>}
NC>>>


NC>>
NC>>public class CarManager : ICarManager
NC>>{
NC>>  public Car getAll(){return null;}
NC>>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который еделен без принадлежности к интерфейсам*/}
NC>>  ...
NC>>}
NC>>


JP>Только что попробовал. Результат: "Cannot implicitly convert type 'System.Collections.Generic.List<Car>' to 'System.Collections.Generic.List<IThing>'... Мне нужен именно список...


Это называется ковариантность возвращаемых значений. (covariance of return type) — хотя в точности определния я могу и ошибатся.
В Яве такая возможность есть в C# к сожалению нету (Опять же в самом CLR это есть, а вот компилятор C# на это ругается, в С# 4,0 появится ковариантность generic'ов)

Но в C# есть ковариантность массивов. Т.е.

Car[] cars = new Car[..];

Можно преобразовать к IList<IThing>.

IList<IThing> things = cars;

Я обходился этим решением, т.е. либо всегда использовал массивы и их возвращал как IList<Базовый тип>
Либо вызывал у колекции ToArray(), а потом уже возвращал.
Re: Наследование и переопределение методов - где ошибка?
От: Ravlyk Австралия http://stitcharteasy.com
Дата: 28.05.09 11:39
Оценка:
Не совсем ответ на вопрос...
Используйте generic:

public interface IThing
{
    String Name { get; }
}

public interface IThingManager
{
    IThing GetAll();
    void Add(IThing thing);
}

public interface IThingManager<TThing> : IThingManager
    where TThing : IThing
{
    new TThing GetAll();
    void Add(TThing thing);
}

public abstract class Thing : IThing
{
    public abstract string Name { get; }
}

public abstract class  ThingManager<TThing> : IThingManager<TThing>
    where TThing : Thing
{
    public abstract TThing GetAll();
    public abstract void Add(TThing thing);

    IThing IThingManager.GetAll() { return GetAll(); }
    void IThingManager.Add(IThing thing) { Add((TThing)thing); }
}

public class Car : Thing
{
    String color;
    int cost;
    public override string Name { get { throw new NotImplementedException(); } }
}

public class CarManager : ThingManager<Car>
{
    public override Car GetAll() { throw new NotImplementedException(); }
    public override void Add(Car thing) { throw new NotImplementedException(); }
}


(Некоторые классы/интерфейсы могут быть лишними, или еще нужно добавить — зависит от специфики задач.)
Re[2]: Наследование и переопределение методов - где ошибка?
От: J-Pro Молдова  
Дата: 28.05.09 12:25
Оценка:
Здравствуйте, Ravlyk, Вы писали:

R>Не совсем ответ на вопрос...

R>Используйте generic:

R>
R>public interface IThing
R>{
R>    String Name { get; }
R>}

R>public interface IThingManager
R>{
R>    IThing GetAll();
R>    void Add(IThing thing);
R>}

R>public interface IThingManager<TThing> : IThingManager
R>    where TThing : IThing
R>{
R>    new TThing GetAll();
R>    void Add(TThing thing);
R>}

R>public abstract class Thing : IThing
R>{
R>    public abstract string Name { get; }
R>}

R>public abstract class  ThingManager<TThing> : IThingManager<TThing>
R>    where TThing : Thing
R>{
R>    public abstract TThing GetAll();
R>    public abstract void Add(TThing thing);

R>    IThing IThingManager.GetAll() { return GetAll(); }
R>    void IThingManager.Add(IThing thing) { Add((TThing)thing); }
R>}

R>public class Car : Thing
R>{
R>    String color;
R>    int cost;
R>    public override string Name { get { throw new NotImplementedException(); } }
R>}

R>public class CarManager : ThingManager<Car>
R>{
R>    public override Car GetAll() { throw new NotImplementedException(); }
R>    public override void Add(Car thing) { throw new NotImplementedException(); }
R>}
R>


R>(Некоторые классы/интерфейсы могут быть лишними, или еще нужно добавить — зависит от специфики задач.)


Спасибо. Так я и сделал вчера по совету своего коллеги. Но этот подход, ессно, не позволяет мне получить объект класса CarManager в виде самого базового интерфейса IThingManager. Только я делал так:
public interface IThingManager<T>
{
  T getAll();
  void add(T thing);
}

public interface ICarManager : IThingManager<Car>
{
  Car getAll();
  void add(Car thing);
}

public class CarManager : ICarManager
{
  Car getAll() {};
  void add(Car thing) {};
}


И всё же было бы приятно видеть ковариантность возвращаемых значений и в C#, я бы несомненно ею воспользовался.

Спасибо за ответ.
-= J-Pro =- with respect
Re[5]: Наследование и переопределение методов - где ошибка?
От: J-Pro Молдова  
Дата: 28.05.09 12:27
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, J-Pro, Вы писали:


JP>>Здравствуйте, NovaCxarmulo, Вы писали:


NC>>>>
NC>>>>public class CarManager : ICarManager
NC>>>>{
NC>>>>  Car getAll(){return null;}
NC>>>>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который определен без принадлежности к интерфейсам*/}
NC>>>>  ...
NC>>>>}
NC>>>>


NC>>>
NC>>>public class CarManager : ICarManager
NC>>>{
NC>>>  public Car getAll(){return null;}
NC>>>  IThing IThingManager.getAll() {return getAll();/*Тут будет вызываться getAll который еделен без принадлежности к интерфейсам*/}
NC>>>  ...
NC>>>}
NC>>>


JP>>Только что попробовал. Результат: "Cannot implicitly convert type 'System.Collections.Generic.List<Car>' to 'System.Collections.Generic.List<IThing>'... Мне нужен именно список...


А>Это называется ковариантность возвращаемых значений. (covariance of return type) — хотя в точности определния я могу и ошибатся.

А>В Яве такая возможность есть в C# к сожалению нету (Опять же в самом CLR это есть, а вот компилятор C# на это ругается, в С# 4,0 появится ковариантность generic'ов)

А>Но в C# есть ковариантность массивов. Т.е.


А>Car[] cars = new Car[..];


А>Можно преобразовать к IList<IThing>.


А>IList<IThing> things = cars;


А>Я обходился этим решением, т.е. либо всегда использовал массивы и их возвращал как IList<Базовый тип>

А>Либо вызывал у колекции ToArray(), а потом уже возвращал.

Хм... если в CLR это есть, то могу ли я это использовать в J#?
Спасибо Вам за ответ и совет со списком, попробую.
-= J-Pro =- with respect
Re[3]: Наследование и переопределение методов - где ошибка?
От: Ravlyk Австралия http://stitcharteasy.com
Дата: 28.05.09 12:29
Оценка:
Здравствуйте, J-Pro, Вы писали:

JP>Спасибо. Так я и сделал вчера по совету своего коллеги. Но этот подход, ессно, не позволяет мне получить объект класса CarManager в виде самого базового интерфейса IThingManager.


Почему не позволяет?
CarManager наследуется от ThingManager<TThing>, который имплементирует IThingManager<TThing>, который наследуется от IThingManager. И в TThingManager<TThing> реализованы нетипизированные методы GetAll() и Add().
Все должно работать (но я не проверял).
Re: Наследование и переопределение методов - где ошибка?
От: nikov США http://www.linkedin.com/in/nikov
Дата: 29.05.09 04:57
Оценка:
Здравствуйте, J-Pro, Вы писали:

JP>В Java такой код ошибок не вызывает, поэтому я решил повторить его и в C#, но безуспешно, почему?


В C#, в отличие от Java, нельзя уточнять тип возвращаемого значения у override методов. Кроме того, методы производных интерфейсов никогда не считаются override для методов базовых интерфейсов.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.