33

I've been trying this, but I can't seem to figure this out. I want to do this...

public abstract class SingletonType<TSingleton, TBaseClass> : TBaseClass
    where TSingleton : TBaseClass, new()
    where TBaseClass : class
{
    static TSingleton _singleton;
    public static TSingleton Singleton
        => _singleton ?? (_singleton = new TSingleton());
}

The plan was to use it like this which would sort of 'wrap' the singleton pattern around a base class...

public class SingletonFoo : SingletonType<SingletonFoo, Foo> {
}

However, I keep getting this

Cannot derive from 'TBaseClass' because it is a type parameter

Um... I thought types were exactly what you do derive from!

So what am I missing?

Note: This is, of course, a trivial example as it doesn't add anything helpful, but assume SingletonType has a lot of other logic which isn't relative to the question, hence it was omitted to focus on the question at hand.

Mark A. Donohoe
  • 28,442
  • 25
  • 137
  • 286
  • 1
    You say, “Um... I thought types were exactly what you do derive from!” — Yes, but read the message again. It says **“type parameter”,** not “type”. That’s a difference. – Timwi May 04 '11 at 22:22
  • Yet you can use that 'Type parameter' everywhere in the generic that you use a type (e.g. 'public static T') so how is using it the way I want different from using it as a return type? (So far @Tejs seems to have the most insight.) – Mark A. Donohoe May 04 '11 at 22:24
  • It might be worth noting that composition would be ideal here as opposed to inheritance. – Mohamed Nuur May 04 '11 at 22:24
  • @Mohamed Nuur, Care to elaborate? (...or can anyone?) Not sure what you mean. – Mark A. Donohoe May 04 '11 at 22:33
  • @MarqueIV: “so how is using it the way I want different from using it as a return type” — it is different because the spec says that the base type cannot be a type parameter, but it says that the return type can. That’s all there is to it. – Timwi May 04 '11 at 22:36
  • possible duplicate of [Why cannot C# generics derive from one of the generic type parameters like they can in C++ templates?](http://stackoverflow.com/questions/1842636/why-cannot-c-generics-derive-from-one-of-the-generic-type-parameters-like-they-c) – Eric Lippert May 04 '11 at 22:58
  • 1
    Possible duplicate of [Why cannot C# generics derive from one of the generic type parameters like they can in C++ templates?](https://stackoverflow.com/questions/1842636/why-cannot-c-sharp-generics-derive-from-one-of-the-generic-type-parameters-like) – Ken Kin Jan 09 '18 at 12:38

4 Answers4

33

Generic types in C# are not C++ templates; remember, a generic type must work for all possible type arguments. A template need only work for the constructions you actually make.

This question is a duplicate; see my answer to

Why cannot C# generics derive from one of the generic type parameters like they can in C++ templates?

for more thoughts on this. Basically, the short answer is that the considerable costs do not outweigh the small benefits of the feature. If you don't like that answer, see my second answer:

Why cannot C# generics derive from one of the generic type parameters like they can in C++ templates?

And if you don't like that answer either, see the follow-up question:

What are the good reasons to wish that .NET generics could inherit one of the generic parameter types?

Community
  • 1
  • 1
Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
20

No, this is not possible. For example, take a type that is declared sealed. You can't inherit from that class, and there is no constraint to limit to non sealed types, ergo trying to inherit from it via a generic parameter is impossible.

Tejs
  • 40,736
  • 10
  • 68
  • 86
  • 22
    Well, this doesn’t really explain why — the compiler could just disallow the use of `MyType` where `X` is the sealed type, instead of disallowing the declaration `MyType : T` entirely. But of course the answer is still “it’s not possible” :) – Timwi May 04 '11 at 22:23
  • 1
    Well, the why is that the generic parameter must be valid for all types allowed; for example, the `new()` constraint allows you to write `T sample = new T()`, because otherwise your generic parameter could be `int` and that is not valid code. Without a `nosealed` constraint, it would be a violation of the C# spec to inherit something with sealed. Because not all cases are valid for a type, and no constraint exists, it is not valid. The error message IS the compiler disallowing this from happening =D – Tejs May 04 '11 at 22:25
  • Actually @Tejs, you also need the `object` with `new()` (Can't have `new()` without it!) which would preclude using an `int` as you said, but you're right about the lack of a `nosealed` constraint. That's I think the issue. – Mark A. Donohoe May 04 '11 at 22:31
  • 5
    No, the superficial why is that the spec says that the base type cannot be a type parameter, full stop. The deep why that you’re trying to provide is why the spec was written that way. You’re providing a non-explanation for that. – Timwi May 04 '11 at 22:31
  • 2
    (By the way, as a nitpick, `int` *does* have a default constructor and therefore satisfies the `new()` constraint, as do all value types.) – Timwi May 04 '11 at 22:33
  • 1
    @MarqueIV: Your comment makes no sense. There is no `object` constraint. Maybe you mean `class`, but then your comment is wrong: you *can* have a `new()` constraint without a `class` constraint. – Timwi May 04 '11 at 22:33
  • Things I didn't know for $200, Alex! (Thanks @Timwi). BTW, @Mohamed Nuur mentioned composition as opposed to inheritance. Know what he was referring to? – Mark A. Donohoe May 04 '11 at 22:35
  • Sorry. Typo. I did mean `class` as per my code example. Who's nitpicking now?! ;) – Mark A. Donohoe May 04 '11 at 22:38
  • @MarqueIV: He’s referring to [object composition](http://en.wikipedia.org/wiki/Object_composition), but I suspect he’s just throwing the word out. You should consider your specific case to determine whether composition is really what you need. – Timwi May 04 '11 at 22:38
  • @Timwi, that's what I thought, which is why I was scratching my head, since that's in the implementation, not the interface. In other words, I wasn't sure how you could 'composite' having the Singleton member without just duplicating that member as I said in the last para of the Q. That's why I was confused. – Mark A. Donohoe May 04 '11 at 22:41
  • It's amazing... I post several questions on JSON.NET and get practically nothing answer-wise, then this thing posts, and I'm more versed in the details in minutes than any book would've tought me in a day! :) But what I just ended up going with was simply duplicating those two lines and abandoning the generic altogether. Was more an experiment anyway. Thanks All! @Tejs FTW!!! – Mark A. Donohoe May 04 '11 at 22:45
  • The C# spec could make an `unsealed` constraint or equivalent, in which case your scenario would not be an issue. I see no reason they couldn't have allowed this and it would be very useful. – Emperor Eto Sep 05 '20 at 14:39
0

Every type has exactly 1 real, discrete parent class, even when generics are involved. Even when dealing with an open generic type (e.g., typeof(List<>)), you can still find the parent class.

If what you wanted were allowed, this would not be true, typeof(SingletonType<,> would not have a parent type, and this is not allowed.

David Yaw
  • 27,383
  • 4
  • 60
  • 93
-1

No, it's not possible, because let's say you have this class:

class bar {
  int example = 0;
}

Now here's our type parameter class:

class foo<T> : T {
  int example = 5;
}

If we created a variable of type foo<bar> then example would get mixed up.

mekb
  • 554
  • 8
  • 22
  • How would it get mixed up? ‘Bar’ there is a type parameter, not a base class. They are completely unrelated and in different scopes. – Mark A. Donohoe Jun 14 '19 at 07:08
  • @MarqueIV No, because `T` is a type of `bar` of `foo`. Since the `foo` class inherits from the `T` class (`bar`) then `example` could be `0` or `5`. – mekb Jun 14 '19 at 07:11
  • I’m sorry, but that’s incorrect. They are scoped completely different. You would access one example from the instance of your generic while you would access the other example from whatever instance variable inside your generic that’s defined via T. They are not the same thing. – Mark A. Donohoe Jun 14 '19 at 07:13
  • 1
    You don't seem to understand how inheritance works in C#. `example` is private in `bar` so a derived class from `bar` can declare its own `example` without issue. Even if `example` were not private, then `foo`'s `example` would be treated as `new`. – Emperor Eto Sep 05 '20 at 14:37
  • Ohh I see now, sorry for bothering – mekb Sep 17 '20 at 02:53