3

Today my brain went dead, and I couldn't figure out a clean way of forcing the compiler to use inheritance for Generic inference.

Imagine the following 4 classes

Models

public abstract class Model
{

}

public class CodePerfModel : Model
{

}

Entities

public abstract class ModelEntity<TModel> where TModel : Model
{
    public TModel Model { get; set; }

}

public class CodePerfEntity : ModelEntity<CodePerfModel>
{

}

Now to me logically I should take for granted that when I take something that inherits from ModelEntity<> (it will specify the type of TModel) via inheritance, because any class that inherits from ModelEntity<> will have to specify it.

Is there anyway to force the compiler to figure this out for me?

E.g.

If I currently want to use ModelEntity<>, I have to specify a type for it. Such as the following:

public class CallerClass<TEntity, TModel>
    where TEntity : ModelEntity<TModel>
    where TModel : Model
{

}

How can I get rid of the TModel argument everywhere? While still having access to the TModel type at compile time? E.g. via the base Model property.

To me, something like the following:

public class CallerClass<TEntity>
    where TEntity : ModelEntity<>
{

}

Would make perfect sense as when calling it all I should have to speicfy is e.g.

SomeCall<CodePerfEntity>();

rather than

SomeCall<CodePerfEntity, CodePerfModel>();

Is this something that is currently possible?

Would this be worth raising for C# 6/7?

Michal Ciechan
  • 13,492
  • 11
  • 76
  • 118
  • "Now to me logically I should take for granted that when I take something that inherits from ModelEntity<> (it will specify the type of TModel) via inheritance, because any class that inherits from ModelEntity<> will have to specify it." sentence does not parse. – Blorgbeard Apr 28 '15 at 22:34
  • Typically, the compiler forces you to be explicit about everything it reasonably can. Is there a use-case that makes the multiple-generic-argument form unusable? – Matthew Haugen Apr 28 '15 at 22:34
  • That sounds like it would get complex fast. Also: what do you want to happen when you do something evil like make TModel is constrained to inherit from CodePerfEntity. I don't see how allowing your both greater complexity with less declaration would be a plus. – Nathan Cooper Apr 28 '15 at 22:36
  • 2
    Ahh here we go. Duplicate: [Why aren't generic type constraints inheritable/hierarchically enforced](http://stackoverflow.com/questions/8606390/why-arent-generic-type-constraints-inheritable-hierarchically-enforced) – Nathan Cooper Apr 28 '15 at 22:37
  • Why even ask for `TModel`? Your entity class is already defining the model type and doesn't seem that relevant. Especially if you go the route of working with an interface if you need to return model instances back. – TyCobb Apr 28 '15 at 22:38
  • @NathanCooper interesting read, god know why i didn't stumble upon it while trying to search. Although i wouldn't go as far as to say it is duplicate as I am not trying to inherit type constraints, only actual types. – Michal Ciechan Apr 28 '15 at 22:55
  • As an aside... Although seemingly acceptable in the US (I've heard it used in Adventure Time!), the term "spaz" is considered offensive in the UK. http://en.wikipedia.org/wiki/Spastic#United_Kingdom – spender Apr 28 '15 at 22:55
  • @TyCobb I need the TModel because that is my vanilla model, no EF annotations, navigation properties etc. It is what gets distributed across the wire. The reason it acts as a base type is because I do not want to be reading + writing all the memory in all the fields again when I want to send it across the wire. – Michal Ciechan Apr 28 '15 at 22:58
  • @MatthewHaugen No, not that I stumbled upon, but it forced me to go and add the second parameter everywhere, which made it feel not right, because I am sure I could get it. I guess I might write a blog about it, and figure out what would happen when you do circular type references. – Michal Ciechan Apr 28 '15 at 23:00
  • 1
    @spender I'm from the UK (or at least from the age of 5). And TBH I do not find that word offensive, but its probably just because of the groups of friends I have. But I can see where you are coming from so I changed it. – Michal Ciechan Apr 28 '15 at 23:04
  • 1
    @MichalCiechan I am too and, yes, I too keep bad company ;) but [some people](http://www.theguardian.com/sport/2006/apr/11/golf.tigerwoods) really do get upset by it. – spender Apr 28 '15 at 23:08
  • 1
    @spender lol @ the link haha – Michal Ciechan Apr 28 '15 at 23:12

1 Answers1

0

You mention you would like to access TModel at compilation time, while not explicitly specifying this type when deriving a class. Letting go of your example, and moving to a more general case, this means you would like the semantics to remain the same, however you would not like to explicitly declare the type parameter's own type parameters when declaring a generic constraint.

In essence, you are asking why a specific syntax sugar feature is not implemented.

Let's consider another example:

public class CallerX<A, B> where A : ModelEntity<> where B : ModelEntity<>

From the example in your question, the compiler should insert TModel'1 and TModel'2 as type parameters for A and B respectively. Let's say the feature is implemented. This means that we have created the default situation that TModel'1 and TModel'2 are different types, each having constraints that match the single type. What if I would like to add more constraints to either TModel'1 or TModel'2, or force them to be the same type? Why is this case so special that it deserves its own syntax?

From what I know of the C# team, they have the policy that each new feature starts with "-100 points" and should be really great in order to be considered (see UserVoice for C#).

To summarize:

  • New language features are expensive and add complexity.
  • You are asking for an implicit syntax for which it is unlikely/unclear that it will be the desired situation in most of the cases.
  • Developers will have to learn and understand that an open generic type as a type parameter constraint will insert a hidden and anonymous extra parameter. To me, it is not intuitive that some other type parameter has been added to my type without me having declared it.
Bas
  • 26,772
  • 8
  • 53
  • 86