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?
Remarks. The MakeGenericType method allows you to write code that assigns specific types to the type parameters of a generic type definition, thus creating a Type object that represents a particular constructed type. You can use this Type object to create run-time instances of the constructed type.
You can't inherit from a Generic type argument. C# is strictly typed language. All types and inheritance hierarchy must be known at compile time. . Net generics are way different from C++ templates.
The where clause in a generic definition specifies constraints on the types that are used as arguments for type parameters in a generic type, method, delegate, or local function. Constraints can specify interfaces, base classes, or require a generic type to be a reference, value, or unmanaged type.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With