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 s开发者_Go百科hould 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();
}
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#:
- Champion "Covariant Return Types"
- Proposal: support "type of the current object" as declared return type.
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
.
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?
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
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();
}
}
You need to make the interface generic, like this:
interface IFoo<TClass> where TClass : IFoo<TClass>, class {
TClass Bar();
}
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
}
精彩评论