Могу ли я реализовать интерфейс, который содержит свойство дочернего типа к тому, что требуется для интерфейса? - PullRequest
9 голосов
/ 03 февраля 2011

Я получаю следующую ошибку:

ClassName.PropertyName не может реализовать IClassType.PropertyName, поскольку у него нет соответствующего возвращаемого типа IBasePropertyType

Сейчас, длякод:

public class ClassName : IClassType
{
    public IChildPropertyType PropertyName { get; set; }
}

public interface IClassType
{
    public IBasePropertyType PropertyName { get; set; }
}

public interface IBasePropertyType
{
    // some methods
}

public interface IChildPropertyType : IBasePropertyType
{
    // some methods
}

Есть ли способ сделать то, что я пытаюсь?Я знаю, что проблема связана с ко / противоречивостью, но я не могу понять, как это сделать.

Ответы [ 2 ]

8 голосов
/ 03 февраля 2011

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

  1. сделать ваш интерфейс универсальным
  2. реализовать интерфейс явно.

Если вы сделаете IClassType родовым, вот так:

public interface IClassType<T> where T : IBasePropertyType
{
    public T PropertyName { get; set; }
}

... тогда вы можете реализовать этот интерфейс, используя различные типы свойств:

public class ClassName : IClassType<IChildPropertyType>
{
    public IChildPropertyType PropertyName { get; set; }
}

Другой вариант - оставить интерфейс не универсальным, а иметь базовый базовый тип, который явно реализует интерфейс:

public class ClassBase<T> : IClassType
    where T : IChildPropertyType
{
    IBasePropertyType IClassType.PropertyName { 
        get {return PropertyName;}
        set {PropertyName = (IChildPropertyType)value;}
    }
    T PropertyName {get;set;}
}

Обратите внимание, что этот последний вариант не совсем идеален, поскольку вы должны динамически приводить свойство к данному дочернему типу: хотя вы можете гарантировать, что каждый тип IChildProperty является IBasePropertyType, вы не можете гарантировать, что каждый IBasePropertyType является IChildPropertyType. Однако, если вы можете удалить сеттер из исходного интерфейса или предпринять другие шаги, чтобы гарантировать, что сеттер никогда не будет вызываться с неверным типом в вашем коде, тогда это может сработать.

5 голосов
/ 03 февраля 2011

Вы правы, что это связано с ковариацией;в частности, это связано с виртуальным методом, возвращаемым типом ковариации , который не является разновидностью ковариации, поддерживаемой языком C #.

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

interface IAnimal {}
interface IGiraffe : IAnimal {}
interface ITiger: IAnimal {}
class Tiger : ITiger {}
interface IHaveAnAnimal { IAnimal Animal { get; set; } }
class C : IHaveAnAnimal
{
    public IGiraffe Animal { get; set; }
}
...
IHaveAnAnimal x = new C();
x.Animal = new Tiger(); // Uh oh. We just put a Tiger into a property of type IGiraffe.

Даже если ковариация была бы вообще законной, этот вид ковариации не был бы законным;вам нужно иметь без установщика , чтобы ковариация была допустимой.

Предположим, что у вас нет установщика:

interface IAnimal {}
interface IGiraffe : IAnimal {}
interface ITiger: IAnimal {}
class Tiger : ITiger {}
interface IHaveAnAnimal { IAnimal Animal { get; } }
class C : IHaveAnAnimal
{
    public IGiraffe Animal { get; }
}

К сожалению, это все еще не разрешено,Но вы можете сделать это:

class C : IHaveAnAnimal
{
    IAnimal IHaveAnAnimal.Animal { get { return this.Animal; } }
    public IGiraffe Animal { get; }
}

Теперь, когда C используется как C, Animal возвращает жирафа, а когда используется IHaveAnAnimal, он возвращает IAnimal.

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