Как определить тип возвращаемого значения метода интерфейса для другого интерфейса? - PullRequest
12 голосов
/ 16 марта 2009

Я новичок в интерфейсах и абстрактных классах. Я хочу создать пару интерфейсов для определения основных методов и переменных для объектов для системы корзины покупок. Затем я хочу создать абстрактные классы, которые реализуют основные функции. Идея состоит в том, что они могут использоваться другими классами немного по-разному для разных проектов.

Вот мои (урезанные) интерфейсы:

public interface ICart
{
    ...
    List<ICartItem> CartItems { get; set; }
}

public interface ICartItem
{
    int ProductId { get; set; }
    int Quantity { get; set; }
}

А вот мой абстрактный класс Cart (опять же, показывающий только соответствующие строки), который реализует ICart:

public abstract class Cart : ICart
{

    private List<CartItem> _cartItems = new List<CartItem>();

    public List<CartItem> CartItems 
    {
        get
        {
            return _cartItems;
        }
    }
}

И мой класс CartItem, который реализует ICartItem:

public abstract class CartItem : ICartItem
{
    ...
}

Когда я пытаюсь скомпилировать классы, я получаю сообщение об ошибке: «Корзина» не реализует элемент интерфейса «CartItems». «Cart.CartItems» не может реализовать «ICart.CartItems», поскольку у него нет соответствующего возвращаемого типа System.Collections.Generic.List .

Я подумал, что идея здесь в том, что интерфейс может быть реализован несколькими классами, которые выполняют основные функции по-разному и добавляют новые методы и т. Д. Почему моему интерфейсу нужно знать, какой класс на самом деле используется, только если этот класс действительно реализует интерфейс правильно?

Ответы [ 6 ]

20 голосов
/ 16 марта 2009

Вам нужно использовать дженерики в C # 2 для достижения этого:

public interface ICart<T> where T : ICartItem
{
    // ...
    List<T> CartItems { get; set; }
}

public abstract class Cart : ICart<CartItem>
{
    // ...
    public List<CartItem> CartItems { get; set; }
}
4 голосов
/ 16 марта 2009

В вашем абстрактном классе вы должны определить, что тип возвращаемого значения свойства CartItems имеет тип List<ICartItem>.

Но, если вы действительно хотите, чтобы свойство имело тип List<CartItem>, вы можете сделать это:

public interface ICart<TCartItem> where TCartItem : ICartItem
{
   List<TCartItem> CartItems { get; set; }
}

public interface ICartItem
{
}

public abstract class Cart : ICart<CartItem>
{
     public List<CartItem> { get; set; }
}

public abstract class CartItem : ICartItem
{
}
3 голосов
/ 16 марта 2009

Это вопрос контра / ковариации.

2 изменения необходимы для компиляции.

1) Удалите опцию «set» в свойстве интерфейса. (Это только реализация get; свойство, которое имеет смысл в любом случае)

2) Изменить корзину на:

<pre> public abstract class Cart : ICart {</p> <pre><code>private List<CartItem> _cartItems = new List<CartItem>(); public List<ICartItem> CartItems { ...

Я также настоятельно рекомендую изменить ваш интерфейс, чтобы отображать IList вместо List. Руководства по дизайну (и FxCop) рекомендуют не выставлять List в открытом интерфейсе. Список - это деталь реализации - IList - это соответствующий интерфейс / тип возврата.

2 голосов
/ 16 марта 2009

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

Чтобы вернуть список элементов, которые реализуют ICartItem, вам нужно использовать дженерики, как это предложено DrJokepu. Это говорит всем, что интерфейс поддерживает только список объектов, реализующих ICartItem, а не ICartItem специально.

public interface ICart<T> where T : ICartItem
{
    List<T> CartItems { get; set; }
}
0 голосов
/ 01 февраля 2012

Есть 2 способа сделать это. Вы можете использовать универсальные шаблоны или явно реализовать элементы интерфейса.

Дженерики

public interface ICart<TCartItem> where TCartItem : ICartItem
{
    ...
    List<TCartItem> CartItems { get; set; }
}

public interface ICartItem
{
    int ProductId { get; set; }
    int Quantity { get; set; }
}

public abstract class Cart : ICart<CartItem>
{

    private List<CartItem> _cartItems = new List<CartItem>();

    public List<CartItem> CartItems 
    {
        get
        {
            return _cartItems;
        }
    }
}
public abstract class CartItem : ICartItem
{
    ...
}

Явно реализованные члены

public interface ICart
{
    ...
    List<ICartItem> CartItems { get; set; }
}
public interface ICartItem
{
    int ProductId { get; set; }
    int Quantity { get; set; }
}

public abstract class Cart : ICart
{
    private List<CartItem> _cartItems = new List<CartItem>();

    List<ICartItem> ICart.CartItems
    {
       get
       {
           return CartItems;
       }
    }
    public List<CartItem> CartItems 
    {
        get
        {
            return _cartItems;
        }
    }
}
public abstract class CartItem : ICartItem
{
    ...
}
0 голосов
/ 16 марта 2009

ICart определяет метод получения и установки для CartItems, но ваша реализация имеет только get.

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