У меня есть ситуация, где у меня есть класс
class Foo
{
Foo Bar()
{
return new Foo();
}
}
Теперь я бледный итог создаю интерфейс для него
class IFoo
{
??? Bar();
}
Что должно быть вместо вопросительных знаков? Каждый класс должен возвратить свой собственный тип, не Foo.
Решения ниже работают, но не делают чистых взглядов. Я не понимаю, почему я должен указать тот же класс дважды, и нет ничего как "это" для текущего типа
Это - то, как я использую его позже
class GenericClass<T> where T : IFoo
{
T foo = new T();
T item = foo.Bar();
}
Вы спрашиваете:
Приведенные ниже решения работают, но не выглядят чистыми. Я не понимаю, почему я должен указывать один и тот же класс дважды, а для текущего типа нет ничего похожего на «this»
. Причина, по которой вы должны указывать его дважды, заключается в том, что в C # отсутствует необходимая функция. Вам нужно что-то вроде этого:
interface IFoo
{
IFoo Bar();
}
class Foo : IFoo
{
Foo Bar() // should work since Foo is an IFoo, but it's not supported by C#
{
return new Foo();
}
}
С точки зрения безопасности типов это должно работать (это называется ковариацией возвращаемого типа ). Фактически, другие языки программирования, такие как C ++ или Java, поддерживают это, см. этот пример в Википедии . К сожалению, ковариация возвращаемого типа не поддерживается C # (даже C # 4.0, который ввел ковариацию для универсальных шаблонов), поэтому вы должны использовать «обходной путь для универсальных шаблонов», показанный в других ответах.
Ковариантные возвращаемые типы, а также тип «this» - это предлагаемые функции для новых версий C #:
Вы можете добавить общий тип и ограничить его, используя тип интерфейса:
public interface IFoo<T>
{
T Bar();
}
Вы бы реализовали это следующим образом:
public class Foo : IFoo<Foo>
{
public Foo Bar()
{
return new Foo();
}
}
public class Cheese : IFoo<Cheese>
{
public Cheese Bar()
{
return new Cheese();
}
}
Обновите , если вас никогда не заботит конкретный тип возвращаемого значения Foo, вы можете сделать следующее:
public interface IFoo
{
IFoo Bar();
}
Что реализовано как:
public class Foo : IFoo
{
public IFoo Bar()
{
return new Foo();
}
}
Затем в вашем универсальном классе:
public class GenericClass<T> where T : class, IFoo, new()
{
public T Rar()
{
T foo = new T();
T item = foo.Bar() as T;
return item;
}
}
GenericClass
будет конкретной реализацией Foo
.
Я думаю, что реальный вопрос: зачем вам нужен производный тип в интерфейсе? Интерфейс как раз по этой причине - абстрагирование от конкретных классов. Если это просто для удобства, так что вам не нужно приводить к Foo после вызова Bar (), вы можете реализовать интерфейс явно:
interface IFoo
{
IFoo Bar();
}
class Foo : IFoo
{
public Foo Bar()
{
return new Foo();
}
IFoo IFoo.Bar()
{
return Bar();
}
}
Задайте себе вопрос: зачем вы вводите интерфейс, когда вам нужен конкретный тип?
public interface IFoo<T>
{
T Bar();
}
Ваша реализация будет выглядеть так:
class Foo : IFoo<Foo>
{
Foo Bar()
{
return new Foo();
}
}
class Baz : IFoo<Baz>
{
Baz Bar()
{
return new Baz();
}
}
Вам нужно сделать интерфейс общим, например так:
interface IFoo<TClass> where TClass : IFoo<TClass>, class {
TClass Bar();
}
Не уверен, чего вы пытаетесь добиться, но это можно сделать так:
interface IFoo<T>
{
T Bar();
}
class Foo:IFoo<Foo>
{
#region IFoo<Foo> Members
public Foo Bar()
{
return new Foo();
}
#endregion
}
Или вот так:
interface IFoo
{
IFoo Bar();
}
class Foo : IFoo
{
#region IFoo Members
public IFoo Bar()
{
return new Foo();
}
#endregion
}
Для этого можно использовать абстрактный базовый класс плюс явную реализацию членов. Сначала объявите свой интерфейс следующим образом:
interface IFoo
{
IFoo Bar();
}
Затем объявите общий абстрактный класс, который реализует IFoo в явном виде, а также объявите абстрактный метод, который как бы "перегружает" Bar(), но в общем виде:
abstract class BaseFooImpl<T> : IFoo where T : BaseFooImpl
{
public abstract T Bar();
IFoo IFoo.Bar()
{
return Bar(); // this will call the abstract Bar()
}
}
Теперь определите свои конкретные классы следующим образом:
class ConcreteFoo : BaseFooImpl<ConcreteFoo>
{
public override ConcreteFoo Bar()
{
return this; // for example, of course.
}
}
Преимущество этого подхода в том, что вы всегда можете использовать негенеративные ссылки IFoo для хранения конкретных экземпляров. Если вы сделаете свой интерфейс общим, вы не сможете, например, объявить следующее:
IFoo mammalInstance, fishInstance; // Instead of IFoo<Mammal> mammalInstance; IFoo<Fish> fishInstance;
List<IFoo> manyInstances; // Instead of List<IFoo<IFoo>>, which doesn't even work AFAIK