Переопределение абстрактного свойства с производным типом возврата в c # - PullRequest
30 голосов
/ 15 июня 2011

У меня есть четыре класса.Запрос, DerivedRequest, Обработчик, DerivedHandler.Класс Handler имеет свойство со следующим объявлением:

public abstract Request request { get; set; }

DerivedHandler должен переопределить это свойство, чтобы вместо этого он возвращал DerivedRequest:

public override DerivedRequest request { get; set; }

Есть ли у кого-нибудь идеи о том,сделать эту работу?

Ответы [ 6 ]

18 голосов
/ 15 июня 2011

Это не очень хороший способ структурировать вещи. Выполните одно из следующих действий

1) Просто не меняйте тип возвращаемого значения и обычно переопределяйте его в подклассе. В DerivedHandler вы можете вернуть экземпляр DerivedRequest, используя подпись базового класса Request. Любой клиентский код, использующий это, может при желании привести его к DerivedRequest.

2) Вместо этого используйте дженерики, если они не должны быть полиморфными.

public abstract class HandlerBase<T> where T: Request
{
    public abstract T Request {get;set;}
}

public class Handler: HandlerBase<Request>()

public class DerivedHandler: HandlerBase<DerivedRequest>()
6 голосов
/ 15 июня 2011

На языке C # вам не разрешено изменять сигнатуру унаследованного метода, если вы не замените его другим методом с тем же именем .Этот метод называется «скрытием члена» или «теневым копированием».

Если вы используете .NET 2.0 или более позднюю версию, вы можете решить эту проблему, превратив возвращаемый тип свойства Request в универсальный.параметр типа класса Handler.Класс DerivedHandler будет затем указывать класс DerivedRequest в качестве аргумента для этого параметра типа.

Вот пример:

// Handler.cs
public class Handler<TRequest> where TRequest : Request
{
    public TRequest Request { get; set; }
}

// DerivedHandler.cs
public class DerivedHandler : Handler<DerivedRequest>
{
}
5 голосов
/ 15 июня 2011

За исключением скрытия исходного свойства:

public new DerivedRequest Request { get;set;}

Однако я настоятельно советую против этого. Скрытие чего-то, что должно быть переопределено, создает проблемы, особенно если свойство не является простым автоматически сгенерированным. Также, если использовать его в качестве интерфейса или базового класса, исходную реализацию (в этом случае на один класс выше в дереве наследования). Если вы реализуете абстрактный класс или интерфейс, вы даже не сможете скрыть оригинальную подпись, так как вам необходимо ее реализовать.

Обычно, если вы думаете об использовании ключевого слова new, вы на неправильном пути. В некоторых случаях это необходимо и необходимо, однако в большинстве случаев это не так.

Вместо этого создайте другое свойство:

public DerivedRequest DerivedRequest {/* make adequate conversions here*/ }

Таким образом, вы четко понимаете, что такое ООП, и вы получаете ясную информацию.

1 голос
/ 15 июня 2011

Редактировать: Вы не можете изменить тип для производного типа, но new может помочь:

В производном типе ...

public new DerivedRequest request
{
   get{return (DerivedRequest) base.request;}
   set{base.request = value;}
}
public override Request request
{
   get{return base.request;}
   set{base.request = (DerivedRequest) value;} // Throws InvalidCastException if misused.
}
0 голосов
/ 15 июня 2011
public class Request{}

public class DerivedRequest : Request{}

public class Handler<T>
  where T : Request
{
  public abstract T Request { get; set; }
}

public class DerivedHandler : Handler<DerivedRequest>
{
  public override DerivedRequest Request { get; set; }
}
0 голосов
/ 15 июня 2011

Это теоретически невозможно. Переопределение должно быть ковариантным для типа возвращаемого значения (то есть тип возвращаемого значения должен быть более конкретным или одинаковым) и противоположным для параметра (то есть тип параметра должен быть менее конкретным или одинаковым). Таким образом, ваш новый тип должен быть одновременно одновременно ковариантным и контравариантным по отношению к Request - это означает, что единственным возможным типом является просто Request.

По этой причине в C # запрещено изменять тип свойств для переопределений.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...