ICollection в интерфейсе с другим интерфейсом в качестве типа

Можно ли указать интерфейс как тип универсального свойства коллекции другого интерфейса, а затем реализовать его с помощью определенного класса? Вот пример того, что я пытаюсь сделать:

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

и определение фактических классов в другом проекте:

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

В IMembershipPrincipal у меня есть доступ только к определению IEmail, но я хотел бы реализовать ICollection с использованием электронной почты вместо IEmail. В настоящее время я получаю сообщение об ошибке:

Ошибка 1 «Пользователь» не реализует элемент интерфейса «IMembershipPrincipal.Emails». «User.Emails» не может реализовать «IMembershipPrincipal.Emails», поскольку он не имеет соответствующего типа возвращаемого значения «System.Collections.Generic.ICollection».

Как изменить определение IMembershipPrincipal, чтобы его можно было реализовать с помощью ICollection<Email>?

Просто для ясности: я понимаю, что реализация виртуальной коллекции как ICollection<IEmail> приведет к сборке кода, но это уничтожит мои пути в Entity framework, так что я не смогу сделать что-то вроде MyUser.Emails.Where(...).


person Dusty    schedule 18.02.2014    source источник
comment
Вы не можете. ICollection<T> не является ковариантным, поэтому вы не можете добавить экземпляр Email к ICollection<IEmail>. Это по дизайну.   -  person MarcinJuraszek    schedule 18.02.2014
comment
Могу ли я использовать что-то другое, кроме ICollection, которое является ковариантным?   -  person Dusty    schedule 18.02.2014
comment
Нет. Невозможно получить ковариантную коллекцию, которая допускает операцию Add.   -  person MarcinJuraszek    schedule 18.02.2014
comment
Я отредактировал ваш заголовок. См. Должны ли вопросы включать «теги» в свои заголовки?, если нет единого мнения, не следует.   -  person John Saunders    schedule 18.02.2014
comment
не обман, а дополнительная информация о том, почему ICollection не может быть ковариантным: -илист   -  person Michael Edenfield    schedule 18.02.2014
comment
ICollection‹T› имеет как ковариантные, так и контравариантные методы. Таким образом, реализация T в вашем конкретном классе должна быть как суперклассом, так и подклассом вашего исходного T. Единственный способ, которым вы можете заполнить это, - это действительно быть T.   -  person Aron    schedule 18.02.2014


Ответы (1)


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

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; }
}
person CD Waddell    schedule 18.02.2014
comment
Работал как шарм. Универсальные типы — это именно то, что мне было нужно. Спасибо! - person Dusty; 18.02.2014