2

Is it possible to specify an interface as the type for a generic collection property of a different interface and then implement it with a specific class? Here is an example of what I am trying to do:

public interface IMembershipPrincipal
{
    int ID { get; set; }
    string UserName { get; set; }
    ICollection<IEmail> Emails { get; set; }
}

public interface IEmail
{
    string Address { get; set; }
    int UserID { get; set; }
}

and defining the actual classes in another project:

public class User : IMembershipPrincipal
{
    [Key]
    public int ID { get; set; }
    public string UserName { get; set; }
    public virtual ICollection<Email> Emails { get; set; }
}

public class Email : IEmail
{
    [Key]
    public string Address { get; set; }
    public int UserID { get; set; }
}

In the IMembershipPrincipal I only have access to the IEmail definition, but I would like to implement the ICollection using Email instead of IEmail. I am currently getting an error that says:

Error 1 'User' does not implement interface member 'IMembershipPrincipal.Emails'. 'User.Emails' cannot implement 'IMembershipPrincipal.Emails' because it does not have the matching return type of 'System.Collections.Generic.ICollection'.`

How can I change my IMembershipPrincipal definition so that it can be implemented with ICollection<Email>?

Just to be clear, I understand that implementing the virtual collection as ICollection<IEmail> will make the code build, but it destroys my paths in Entity framework so that I can't do something like MyUser.Emails.Where(...).

Aron
  • 15,464
  • 3
  • 31
  • 64
Dusty
  • 623
  • 5
  • 11
  • You can't. `ICollection` is not covariant, so you cannot add `Email` instance to `ICollection`. That's by design. – MarcinJuraszek Feb 18 '14 at 01:20
  • Could I use something other than ICollection that is covariant? – Dusty Feb 18 '14 at 01:22
  • 1
    No. There is no way to get covariant collection which allows `Add` operation. – MarcinJuraszek Feb 18 '14 at 01:22
  • I have edited your title. Please see, "[Should questions include “tags” in their titles?](http://meta.stackexchange.com/questions/19190/)", where the consensus is "no, they should not". – John Saunders Feb 18 '14 at 01:27
  • not a dupe but more info on why ICollection can't be covariant: http://stackoverflow.com/questions/5832094/covariance-and-ilist – Michael Edenfield Feb 18 '14 at 01:36
  • ICollection has both Co-variant and Contra-variant methods. Thus the implementation of T in your concrete class must be both a superclass and a subclass of your original T. The only way you can forefill this, is to actually be T. – Aron Feb 18 '14 at 01:50

1 Answers1

3

As mentioned by others, covariance keeps you from using the code from your example. You however, can use generic types and constraints to get the desired effect.

public interface IMembershipPrincipal<T> where T:IEmail
{
    int ID { get; set; }
    string UserName { get; set; }
    ICollection<T> Emails { get; set; }
}

public interface IEmail
{
    string Address { get; set; }
    int UserID { get; set; }
}

public class User : IMembershipPrincipal<Email>
{
    [Key]
    public int ID { get; set; }
    public string UserName { get; set; }
    public virtual ICollection<Email> Emails { get; set; }
}

public class Email : IEmail
{
    [Key]
    public string Address { get; set; }
    public int UserID { get; set; }
}
CD Waddell
  • 248
  • 1
  • 13