Restrict generic parameter on interface to subclass
Asked Answered
O

2

11

The following is contrived, but bear with me:

interface Clonable<TSubClass>
{
    TSubClass Clone();
}

How can I restrict TSubClass to be of the implementing type?

i.e only let the implementor do this:

class Dog : Clonable<Dog>
{
    Dog Clone() 
    {
        ....
    }
}

Not this:

class BadDog : Clonable<Rabbit>
{
    Rabbit Clone()
    {
        ....
    }
}
Overword answered 19/7, 2012 at 21:49 Comment(0)
C
3

You can't enforce that at compile-time, because .NET generics don't have template specialization or duck typing.

You can, however, include a static constructor (type initializer) that uses reflection to assert the relationship at load time. Ok, C# doesn't allow you to put a static constructor on an interface (even though .NET does allow it), so you would need to use a module initializer or a function you call yourself. Also, you would need to search for types implementing the interface, including types that aren't loaded yet (you can subscribe to the Assembly.Load event to be notified of types loaded in the future).

Cruce answered 19/7, 2012 at 22:22 Comment(7)
The static ctor idea is great.Nemathelminth
-1 The static constructors would have to be defined in the concrete classes themselves, which would be totally redundant and unnecessary with the declaration of the self-type parameter itself.Twitch
@Jordão: That's not true, reflection can discover all classes inheriting from the base class or implementing the interface.Cruce
@BenVoigt: OK, but that starts to be a little bit ... cumbersome. You'll have to look for all classes in all loaded assemblies if they inherit from you. And ... you can't add a static constructor to an interface in C#.Twitch
@Jordão: I was thinking more of a base class. Interfaces can have type initializers in .NET (although the C# compiler doesn't help with that) but they don't run at a convenient time. But arranging for a piece of code to run isn't that difficult. And there's an event that runs when new assemblies are loaded.Cruce
True, and you could even use a module initializer. But still, it's not as trivial as your answer purports it to be... It takes a lot of non-trivial effort (look for all concrete subclasses and subscribe to an event that runs when assemblies are loaded). Add these bits of information and I'll be able to remove my downvote :-)Twitch
Absolutely! I removed my downvote. I'm sorry to say that I won't upvote because I still think it's more trouble than it's worth....Twitch
L
9

You can't enforce that, only through convention and documentation....

The convention for me would be to use something like TSelf.

interface ICloneable<TSelf> where TSelf : ICloneable<TSelf> { ... }

Also note that any non-concrete construct that implements or inherits this interface should pass the constraint through...

[Serializable]
abstract class SerializableCloneable<TSelf> : ICloneable<TSelf> 
  where TSelf : SerializableCloneable<TSelf> { ... }

Note: I've implemented this check in NRoles using the convention of calling your self-type parameter S.

Lisbethlisbon answered 19/7, 2012 at 21:54 Comment(2)
You can enforce it at runtime, too. See Ben Voigt' answer.Nemathelminth
@usr: that won't work, only if you add the static constructor to the concrete classes themselves, which would be totally redundant.Twitch
C
3

You can't enforce that at compile-time, because .NET generics don't have template specialization or duck typing.

You can, however, include a static constructor (type initializer) that uses reflection to assert the relationship at load time. Ok, C# doesn't allow you to put a static constructor on an interface (even though .NET does allow it), so you would need to use a module initializer or a function you call yourself. Also, you would need to search for types implementing the interface, including types that aren't loaded yet (you can subscribe to the Assembly.Load event to be notified of types loaded in the future).

Cruce answered 19/7, 2012 at 22:22 Comment(7)
The static ctor idea is great.Nemathelminth
-1 The static constructors would have to be defined in the concrete classes themselves, which would be totally redundant and unnecessary with the declaration of the self-type parameter itself.Twitch
@Jordão: That's not true, reflection can discover all classes inheriting from the base class or implementing the interface.Cruce
@BenVoigt: OK, but that starts to be a little bit ... cumbersome. You'll have to look for all classes in all loaded assemblies if they inherit from you. And ... you can't add a static constructor to an interface in C#.Twitch
@Jordão: I was thinking more of a base class. Interfaces can have type initializers in .NET (although the C# compiler doesn't help with that) but they don't run at a convenient time. But arranging for a piece of code to run isn't that difficult. And there's an event that runs when new assemblies are loaded.Cruce
True, and you could even use a module initializer. But still, it's not as trivial as your answer purports it to be... It takes a lot of non-trivial effort (look for all concrete subclasses and subscribe to an event that runs when assemblies are loaded). Add these bits of information and I'll be able to remove my downvote :-)Twitch
Absolutely! I removed my downvote. I'm sorry to say that I won't upvote because I still think it's more trouble than it's worth....Twitch

© 2022 - 2024 — McMap. All rights reserved.