user582181
user582181

Reputation: 53

Interface as a Property

I have an interface and two classes which implement it. I'm getting a compiler error, and I'm not quite sure why.

interface IPerson
{
    ICollection<string> NickNames{get;set;}
}
class ObservablePerson : IPerson
{
    ObservableCollection<string> NickNames{get;set;}
}
class ListPerson : IPerson
{
    List<string> NickNames{get;set;}
}

I'm having a bit of trouble understanding why this won't work, as List and ObservableCollection both implement ICollection.

Upvotes: 5

Views: 2793

Answers (5)

EnemySkill
EnemySkill

Reputation: 57

I just ran into a similar problem, and a buddy of mine suggested an excellent solution: Generic types. Here's what you can do:

public interface IPerson<CollectionType> where CollectionType : ICollection<string>
{
    CollectionType NickNames { get; set; }
}

class ObservablePerson : IPerson<ObservableCollection<string>>
{
    public ObservableCollection<string> NickNames { get; set; }
}

public class ListPerson : IPerson<List<string>>
{
    public List<string> NickNames { get; set; }
}

Here, "CollectionType" is a Generic, and can be named whatever you want (It's often called T throughout the .NET Framework, but giving it a meaningful name I feel is helpful). This will allow you to implement an interface on those ObservablePerson and ListPerson, while still having different types for the NickNames property on each (though any type that you use for CollectionType will need to implement ICollection).

Upvotes: 1

Ed Swangren
Ed Swangren

Reputation: 124790

I'm having a bit of trouble understanding why this won't work, as List and ObservableCollection both implement ICollection.

Yes, but the interface states that an ICollection<string> is returned. The underlying type may be an ObservableCollection<string> or a List<string>, but the signature needs to conform to the interface. An ObservableCollection<string> is an ICollection<string>, but an ICollection<string> is not necessarily an ObservableCollection<string>.

Also, your methods need to be public (they are currently private). Interfaces don't deal with private or protected methods, it defines the public interface.

Upvotes: 6

Divi
Divi

Reputation: 7701

It should be

interface IPerson
{
    ICollection<string> NickNames{get;set;}
}
class ObservablePerson : IPerson
{
    ICollection<string> NickNames{get;set;}
}
class ListPerson : IPerson
{
    ICollection<string> NickNames{get;set;}
}

And inside the implementation, you can return a List object or ObservableCollection

Edited code

interface IPerson
    {
        ICollection<string> NickNames{get;set;}
    }

public class ObservablePerson : IPerson
    {
        ICollection<string> nickNames = new ObservableCollection<string>();

        public ICollection<string> IPerson.NickNames
        {
            get
            {
                return nickNames;
            }
            set
            {
                nickNames = value;
            }
        }
    }
    public class ListPerson : IPerson
    {
        ICollection<string> nickNames = new List<string>();

        public ICollection<string> IPerson.NickNames
        {
            get
            {
                return nickNames;
            }
            set
            {
                nickNames = value;
            }
        }
    }

Upvotes: 1

Zarat
Zarat

Reputation: 2754

The compiler will insist on exact type matches, but you can shadow the property when accessed on the class (instead of through the interface). Note however, that when setting through the interface someone may assign a different kind of ICollection<string> - so this would result in an exception.

interface IPerson
{
    ICollection<string> NickNames { get; set; }
}
class ObservablePerson : IPerson
{
    public new ObservableCollection<string> NickNames { get; set; }
    ICollection<string> IPerson.NickNames
    {
        get { return NickNames; }
        set { NickNames = (ObservableCollection<string>)value; }
    }
}
class ListPerson : IPerson
{
    public new List<string> NickNames { get; set; }
    ICollection<string> IPerson.NickNames
    {
        get { return NickNames; }
        set { NickNames = (List<string>)value; }
    }
}

Upvotes: 0

Aaronontheweb
Aaronontheweb

Reputation: 8414

Your issue is that the compiler expects both of your subclasses to return a type of ICollection. You can return a List or ObservableCollection via the get accessor, but the declared member type needs to remain ICollection.

Upvotes: 0

Related Questions