Reputation: 2167
Kind of theoretical question. Quite long so feel free to skip if you are not in the mood for theory.
Imagine that you have two classes, one inherited from another. The base class is generic and has a method that in the closed type must return some instance of this closed type.
Like this (note ??? in text):
public class Adapter<T>
{
public virtual ??? DoSomething()
{
...
}
}
public class AdaptedString : Adapter<String>
{
public override AdaptedString DoSomething()
{
...
}
}
I can't do it because there is no way to refer to a closed type that will be derived from a generic type. (Sorry for broken language, just don't know how to express it.) There is no keyword to set in place of ???
to specify that this method will return instance of type that would be derived from this generic type.
Instead, I can use a workaround of explicitly passing the type name to the generic base. But it looks redundant.
public class Adapter<TThis,T>
{
public virtual TThis DoSomething()
{
...
}
}
public class AdaptedString : Adapter<AdaptedString,String>
{
public override AdaptedString DoSomething()
{
...
}
}
And if in the base class I need to access members of TThis
instance, I have to add a constraint. This time it looks ugly - note the constraint:
public class Adapter<TThis,T>
where TThis : Adapter<TThis, T>
{
protected int _field;
...
public bool Compare( TThis obj )
{
return _field == obj._field;
}
}
public class AdaptedString : Adapter<AdaptedString,String>
{
...
}
Yes, it is all working, but it would look better if I can simply use some keyword instead of ???
in first code fragment. Something like "thistype".
How do you think will it work? Is it useful? Or maybe this is just plain stupid?
Upvotes: 6
Views: 4015
Reputation: 1500525
There's nothing which makes this pattern easier, and in fact the pattern isn't quite bulletproof anyway - because you can have:
class TypeA : Adapter<TypeA, string>
class TypeB : Adapter<TypeA, string> // Bug!
The second line here is entirely legal - TypeA
is a valid type argument for the TThis
type parameter, even though it's not what we wanted. Basically the type system doesn't let us express the concept of "T must be this type."
I disagree with those who say it's a bad or useless pattern, however. I've found it useful (if complicated) in Protocol Buffers - which would be much worse off without it. For example:
Foo foo = new Foo.Builder { Name="Jon" }.Build();
wouldn't work if Foo.Build()
wasn't strongly typed to return Foo
, even though the Build
method is specified in IBuilder<...>
.
It's worth avoiding this if you easily can simply because it gets so complicated - but I do think it's a useful pattern to know.
Upvotes: 14
Reputation: 3367
I too am having trouble finding an arguable use case for this (though it is an interesting idea).
Are you trying to shift around how you constrain what generic types you can use? It sounds like you want to assume some base functionality without knowing the actual type; that is what Interfaces are for. The where clause is pretty handy for those kinds of problems.
class Dictionary<K, V>
where K : IComparable, IEnumerable
where V : IMyInterface
{
public void Add(K key, V val)
{
}
}
The above example constrains K (the key) so that it must be comparable and enumerable, and V must implement whatever customer functionality you want via your own interface.
Upvotes: -2
Reputation: 12204
If an inherited method in your derived class needs to return the derived type instead of the base type (known as a covariant return type), this is already supported in C#.
Upvotes: -2
Reputation: 564413
You'll normally just want to refer to the base class in that case:
public class Adapter<T> {
public virtual Adapter<T> DoSomething();
Trying to do what you're accomplishing violates the Liskov substitution principal.
Upvotes: 5