Абстрактная реализация явного интерфейса в С#
У меня есть код С#:
abstract class MyList : IEnumerable<T>
{
public abstract IEnumerator<T> GetEnumerator();
//abstract IEnumerator IEnumerable.GetEnumerator();
}
Как есть, я получаю:
'Тип' не реализует член интерфейса 'System.Collections.IEnumerable.GetEnumerator()'.
удалите комментарий, и я получаю:
Модификатор 'abstract' недействителен для этого элемента
Как сделать абстрактную абстрактную реализацию
Ответы
Ответ 1
Интересный - я не уверен, что вы можете. Однако, если это ваш реальный код, вы когда-нибудь захотите реализовать не-общий GetEnumerator()
любым способом, кроме как вызвать общий?
Я бы сделал это:
abstract class MyList<T> : IEnumerable<T>
{
public abstract IEnumerator<T> GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
Это избавит вас от скуки необходимости реализовать ее в каждом производном классе, что, без сомнения, будет использовать одну и ту же реализацию.
Ответ 2
Хотя явный член интерфейса не может быть абстрактным (или виртуальным), он может быть реализован в терминах абстрактного (или виртуального) элемента 1:
public abstract class Foo: IEnumerable {
IEnumerator IEnumerable.GetEnumerator() {
return getEnumerator();
}
protected abstract IEnumerator getEnumerator();
}
public class Foo<T>: Foo, IEnumerable<T> {
private IEnumerable<T> ie;
public Foo(IEnumerable<T> ie) {
this.ie = ie;
}
public IEnumerator<T> GetEnumerator() {
return ie.GetEnumerator();
}
protected override IEnumerator getEnumerator() {
return GetEnumerator();
}
//explicit IEnumerable.GetEnumerator() is "inherited"
}
Я нашел необходимость этого в строго типизированных ASP.NET MVC 3 частичных представлениях, которые не поддерживают общие модели определения типов (насколько я знаю).
Ответ 3
Фактически вы можете это сделать, заставив класс, который происходит от абстрактного класса, реализовать интерфейс и по-прежнему разрешать ему выбирать, как реализовать этот интерфейс - неявно или явно:
namespace Test
{
public interface IBase<T>
{
void Foo();
}
public abstract class BaseClass<T>
where T : IBase<T> // Forcing T to derive from IBase<T>
{ }
public class Sample : BaseClass<Sample>, IBase<Sample>
{
void IBase<Sample>.Foo() { }
}
class Program
{
static void Main(string[] args)
{
Sample sample = new Sample();
// Error CS1061 'Sample' does not contain a definition for 'Foo'
// and no extension method 'Foo' accepting a first argument of type 'Sample'
// could be found(are you missing a using directive or an assembly reference ?)
sample.Foo();
(sample as IBase<Sample>).Foo(); // No Error
}
}
}
Ответ 4
У меня был несколько более сложный случай, когда я хотел, чтобы базовый класс явно реализовал не общий интерфейс, а производный класс реализовал общий интерфейс.
Интерфейсы:
public interface IIdentifiable<TKey> : IIdentifiable
{
TKey Id { get; }
}
public interface IIdentifiable
{
object Id { get; }
}
Я решил его, объявив абстрактный метод getter в базовом классе и разрешив ему явную реализацию:
public abstract class ModelBase : IIdentifiable
{
object IIdentifiable.Id
{
get { return GetId(); }
}
protected abstract object GetId();
}
public class Product : ModelBase, IIdentifiable<int>
{
public int ProductID { get; set; }
public int Id
{
get { return ProductID; }
}
protected override object GetId()
{
return Id;
}
}
Обратите внимание, что базовый класс не имеет типизированной версии Id
, которую он мог бы вызвать.