Andrey
Andrey

Reputation: 1248

Interface for method that returns its own type

I have a situation where i have a class

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

Now i wan tot create an interface for it

class IFoo
{
    ??? Bar();
}

What should be in place of the question marks? Each class should return it's own type, not Foo.

The solutions below work but do not looks clean. I don't understand why i have to specify the same class twice, and there is nothing like "this" for the current type

This is how i am using it later

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

Upvotes: 9

Views: 5378

Answers (7)

Heinzi
Heinzi

Reputation: 172260

You ask:

The solutions below work but do not looks clean. I don't understand why i have to specify the same class twice, and there is nothing like "this" for the current type

The reason why you have to specify it twice is because C# lacks the feature that you need. What you want is something like this:

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

From a type-safety point of view, this should work (it's called return type covariance). In fact, other programming languages such as C++ or Java support this, see this example on Wikipedia. Unfortunately, return type covariance is not supported by C# (not even C# 4.0, which introduced covariance for generics), which is why you have to use the "generics workaround" illustrated in the other answers.

Covariant return types as well as a "this" type are proposed features for new versions of C#:

Upvotes: 11

djdd87
djdd87

Reputation: 68466

You could add a generic type and constrain it using the interface type:

public interface IFoo<T>
{
    T Bar();
}

You'd implement this as follows:

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

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

Update, if you never care about the concrete return type of Foo, then you can do the following:

public interface IFoo
{
    IFoo Bar();
}

Which is implemented like:

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

Then in your generic class:

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(); will be a concrete implementation of Foo.

Upvotes: 9

Stefan
Stefan

Reputation: 4206

I think that the real question is: why you need the derived type in the interface? Interface is exactly for that reason - abstracting from the concrete classes. If it's just for convenience, so you don't have to cast to Foo after calling Bar(), you can implement the interface explicitly:

interface IFoo
{
    IFoo Bar();
}

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

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

Ask yourself the question: why do you introduce an interface when you want the concrete type?

Upvotes: 4

Humberto
Humberto

Reputation: 7199

You can use an abstract base class plus explicit member implementation to achieve this. First, declare your interface like this:

interface IFoo
{
    IFoo Bar();
}

Then, declare a generic abstract class that implements IFoo in an explicit manner, and also declares an abstract method that kind of "overloads" Bar(), but in a generic manner:

abstract class BaseFooImpl<T> : IFoo where T : BaseFooImpl
{
    public abstract T Bar();

    IFoo IFoo.Bar()
    {
        return Bar(); // this will call the abstract Bar()
    }
 }

Now, define your concrete classes like this:

class ConcreteFoo : BaseFooImpl<ConcreteFoo>
{
   public override ConcreteFoo Bar()
   {
      return this; // for example, of course.
   }
}

The advantage of this approach is that you can always use non-generic IFoo references to hold concrete instances. If you make your interface generic, you can't, for instance, declare these:

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

Upvotes: 2

CkH
CkH

Reputation: 1295

Not sure what you are trying to accomplish but it could be done this way:

interface IFoo<T>
{
    T Bar();
}



   class Foo:IFoo<Foo>
    {

        #region IFoo<Foo> Members

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

        #endregion
    }

Or Like this:

    interface IFoo
    {
        IFoo Bar();
    }

class Foo : IFoo
    {

        #region IFoo Members

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

        #endregion
    }

Upvotes: 0

SLaks
SLaks

Reputation: 887433

You need to make the interface generic, like this:

interface IFoo<TClass> where TClass : IFoo<TClass>, class {
    TClass Bar();
}

Upvotes: 0

Justin Niessner
Justin Niessner

Reputation: 245419

public interface IFoo<T>
{
    T Bar();
}

Your implementation would then be:

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

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

Upvotes: 0

Related Questions