Интерфейс для метода, который возвращает свой собственный тип - PullRequest
8 голосов
/ 15 июня 2010

У меня есть ситуация, когда у меня есть класс

class Foo
{
    Foo Bar()
    {
        return new Foo();
    }
}

Теперь я хочу создать для него интерфейс

class IFoo
{
    ??? Bar();
}

Что должно быть вместо знаков вопроса?Каждый класс должен возвращать свой собственный тип, а не Foo.

Решения, приведенные ниже, работают, но не выглядят чистыми.Я не понимаю, почему мне нужно указывать один и тот же класс дважды, и нет ничего похожего на «this» для текущего типа

. Вот как я использую его позже

class GenericClass<T> where T : IFoo
{ 
    T foo = new T();
    T item = foo.Bar();
}

Ответы [ 7 ]

11 голосов
/ 15 июня 2010

Вы спрашиваете:

Решения, приведенные ниже, работают, но не выглядят чистыми. Я не понимаю, почему я должен указывать один и тот же класс дважды, и нет ничего похожего на "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 #:

8 голосов
/ 15 июня 2010

Вы можете добавить универсальный тип и ограничить его, используя тип интерфейса:

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>.Rar(); будет конкретной реализацией Foo.

4 голосов
/ 15 июня 2010

Я думаю, что реальный вопрос: зачем вам нужен производный тип в интерфейсе?Интерфейс именно по этой причине - абстрагируясь от конкретных классов.Если это просто для удобства, поэтому вам не нужно приводить к Foo после вызова Bar (), вы можете явно реализовать интерфейс:

interface IFoo
{
    IFoo Bar();
}

class Foo : IFoo
{
    public Foo Bar()
    {
        return new Foo();
    }

    IFoo IFoo.Bar()
    {
        return Bar();
    }
}

Задайте себе вопрос: почему вы вводите интерфейс, когдаВы хотите конкретный тип?

2 голосов
/ 15 июня 2010

Вы можете использовать абстрактный базовый класс плюс явную реализацию члена для достижения этой цели.Сначала объявите ваш интерфейс следующим образом:

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
0 голосов
/ 15 июня 2010

Не уверен, что вы пытаетесь достичь, но это можно сделать следующим образом:

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
    }
0 голосов
/ 15 июня 2010

Вам нужно сделать интерфейс универсальным, например:

interface IFoo<TClass> where TClass : IFoo<TClass>, class {
    TClass Bar();
}
0 голосов
/ 15 июня 2010
public interface IFoo<T>
{
    T Bar();
}

Ваша реализация будет тогда:

class Foo : IFoo<Foo>
{
    Foo Bar()
    {
        return new Foo();
    }
}

class Baz : IFoo<Baz>
{
    Baz Bar()
    { 
        return new Baz(); 
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...