简体   繁体   中英

How do I define the return type of an interface method to be another interface?

I'm new to interfaces and abstract classes. I want to create a couple of interfaces to define core methods and variables for the objects for a shopping cart system. Then I want to create abstract classes which implement the core functions. The idea is that they can be used by other classes in slightly different ways for different projects.

Here are my (cut-down) interfaces:

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

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

And here is my abstract Cart class (again, only showing relevant lines) which implements ICart:

public abstract class Cart : ICart
{

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

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

And my CartItem class which implements ICartItem:

public abstract class CartItem : ICartItem
{
    ...
}

When I try to compile the classes I get an error saying: 'Cart' does not implement interface member 'CartItems'. 'Cart.CartItems' cannot implement 'ICart.CartItems' because it does not have the matching return type of System.Collections.Generic.List<ICartItem>.

I thought that the idea here is that the interface can be implemented by a number of classes which perform the core functions in different ways and add new methods, etc. Why would my interface need to know what class is actually being used, just as long as that class actually implements the interface correctly?

You need to user generics in C# 2 to achieve that:

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

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

In your abstract class, you should define that the return type of the CartItems property, is of type List<ICartItem> .

But, if you really want that the property is of type List<CartItem> , you can maybe do this:

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
{
}

This is a matter of contra/covariance.

2 changes are required to make this compile.

1) Remove the "set" option on the property of the interface. (It's only implementing a get; property, which makes the most sense, in any case)

2) Change Cart to:


public abstract class Cart : ICart
{

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

public List<ICartItem> CartItems 
{ ...

I also highly recommend changing your interface to expose IList instead of List. The design guidelines (and FxCop) recommend not exposing List in the public interface. List is an implementation detail - IList is the appropriate interface/return type.

An interface is an agreement. A contract, if you will, between you and anyone who uses your class. By telling folks that you are implementing the ICart interface, you are promising them that all the methods in the interface exist on your class. Thus, all your methods must match the signatures of the interface methods.

In order to return of list of items that implement ICartItem, you need to use generics as suggested by DrJokepu. This tells everyone that the interface only gurentees a list of objects that implement ICartItem, not ICartItem specifically.

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

ICart specifies a getter and setter for CartItems but your implementation only has a get.

There are 2 ways to accomplish this. You can either use generics or explicitly implement interface members.

Generics

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
{
    ...
}

Explicitly implemented members

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
{
    ...
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM