tags:

views:

276

answers:

4

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?

+4  A: 

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.

Reed Copsey
It looks like it was the perfect answer here: http://stackoverflow.com/questions/1400317
dtb
Yes, but that's not the same scenario. They're not working from a base class, but rather from a concrete type.
Reed Copsey
No, Reed this won't work, because I want DoSomething in derived (closed) type to return AdapdedString, not it's base type.
XOR
And I don't agree that it violates Liskov principle. Our base class is open. We anyway can't write code against it, so derived types just don't have anything to break.
XOR
XOR: If that's the case, this works fine. Just don't add it to your base class. The concrete class can have this method. However, IF you add it to the base class, it violates LSP
Reed Copsey
A: 

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#.

Loadmaster
No, because C# doesn't support covariant return types.
Jon Skeet
(And even if it did, that still wouldn't always be enough to help.)
Jon Skeet
Sorry, thought it did.
Loadmaster
+6  A: 

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.

Jon Skeet
+1 I stand corrected :)
Andrew Hare
Jon, second line in your example is just something different, not falling into this pattern. If it is necessary somewhere, it can be achieved by existing syntax. I just think that this syntax makes things in my example even more complicated. I wrote this post after finding bug in our code. I wrote this base class initially some time ago, but I had to reread it several times to get a grip again. No wonder that other guy introduced a bug.
XOR
@XOR: My point is that the second line is *legal* but undesirable (in this case). I'll edit the answer to make that clearer.
Jon Skeet
A: 

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.

Dusda