Основные вопросы по интерфейсам C # - PullRequest
2 голосов

Я хочу реализовать следующую структуру:

public interface IMyDbSet
{
    IEnumerable<User> Users { get; }
}

public class ConcreteDbSet : IMyDbSet
{
    DbSet<User> Users { get; set; }
}

Хотя DbSet<T> наследуется от IEnumerable<T>, это невозможно.

Сообщение об ошибке Visual Studio:

.... не реализует интерфейс член '....'. «...» не может быть реализован «....» потому что он не имеет соответствующий тип возврата '....'.

Есть ли другой способ реализации такой структуры или это вообще невозможно?

Спасибо.

Ответы [ 3 ]

8 голосов
/ 10 мая 2011

Самым простым способом было бы явно реализовать интерфейс и вернуть значение неявного свойства Users из явного свойства Users.

public interface IMyDbSet
{
    IEnumerable<User> Users { get; }
}

public class ConcreteDbSet : IMyDbSet
{
    IEnumerable<User> IMyDbSet.Users 
    {
        get { return Users; }
    }

    DbSet<User> Users 
    { 
        get; set; 
    }
}
2 голосов
/ 10 мая 2011

Это невозможно, потому что тот, кто призывает IMyDbSet.Users, ожидает получить IEnumerable<User>, что может быть, но не обязательно, DbSet<User>. DBSet<T> - это IEnumerable<T>, но IEnumerable<T> - это не DbSet<T>.

Это, как говорится, ничего не мешает вам вернуть DbSet<USer> экземпляр как IEnumerable<User>

1 голос
/ 10 мая 2011

Функция, которую вы хотите использовать, называется " тип возвращаемой ковариации ", и это недопустимо (к сожалению) в языке программирования C #.

В некоторых других языках, таких как C ++, Java или Eiffel (и некоторых других), вы можете использовать более конкретный тип при переопределении виртуального метода или реализации интерфейса.

Если вы имеете дело с интерфейсом, вы можете использовать известную идиому (как упомянул Флориан): вы можете явно реализовать свой интерфейс и добавить другой метод с другой сигнатурой.

public interface IMyDbSet
{
    IEnumerable<User> Users { get; }
}

public class ConcreteDbSet : IMyDbSet
{
    IEnumerable<User> IMyDbSet.Users {get {return Users;}}

    public DbSet<User> Users { get; set; }
}

Другой вариант - универсальный пользователь IMyDbSet<T> аналогично bool Equals(object rhs) из System.Object и универсальный типобезопасный интерфейс [IEquatable<T>][2]:

public interface IMyDbSet<T> where T : IEnumerable<User>
{
   // Because T is IEnumerable<User> or one of it descendant
   // this property is similar to IEnumerable<User> Users {get;}
   T Users {get;}
}

// Now we're implementing not IMyDbSet interface itself
// but we're IMyDbSet<IList<User>> instead.
// Note you could use your own descendant for IEnumerable<User> here
public class ConcreteDbSet : IMyDbSet<IList<User>>
{
    public IList<User> Users {get; set;}
}


//...
// and now you could use ConcreteDbSet following way:
var set = new ConcreteDbSet();
IList<User> users = set.Users;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...