Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inference from Generic Type Question

I suppose this is more of a public rant, but why can't I get c# to infer my Id's type?

public EntityT Get<EntityT>(IdT id) where EntityT : EntityObject<IdT>

and a defined EntityObject with a Guid as an Id as follows:

public Foo : EntityObject<Guid>

Inheriting from the abstract EntityObject class defined as follows:

public abstract class EntityObject<IdT>
{
    public IdT id { get; set; }
}

Usage of the get method would be as follows:

IRepository repository = new Repository();
var hydratedFoo = repository.Get<Foo>(someGuidId);

edited to provide further clarification.

like image 868
Raspar Avatar asked Aug 11 '09 17:08

Raspar


People also ask

What is type inference in Java?

Type inference is when the compiler can look at the type of a method argument to infer a generic type. For example, if we passed in T to a method which returns T, then the compiler can figure out the return type. Let's try this out by invoking our generic method from the previous question:

How do you determine if an inference is correct?

If you are asked a question about a text that you've read thoroughly, just about any inference you make, as long as it is supported by evidence and answers the question in its entirety, can be considered correct.

What is a generic method?

A generic method is where a type parameter is introduced to a method, living within the scope of that method. Let's try this with an example: We've used a static method but could have also used a non-static one if we wished.

Why do we need to implement generic types?

The reasoning behind major implementation choice is simple – preserving backward compatibility with older versions of Java. When a generic code is compiled into bytecode, it will be as if the generic type never existed. This means that the compilation will:


1 Answers

It's hard to say given that you've only given two declarations, not how you're using them. Is IdT another type parameter somewhere? (If it were TId, that would suggest it is - but the fact that you're using EntityT for another type parameter, contrary to conventions, suggests that maybe IdT is as well...)

Now, assuming IdT is actually Guid in your case, how should the compiler work out that you mean Foo? There could be other types deriving from EntityObject<Guid>.

In short, you haven't given us enough information to tell anything for sure, but it sounds like you're basically making unreasonable demands on the compiler.

EDIT: Okay, here's my guess at what you have, using normal naming conventions:

public interface IRepository
{
    TEntity Get<TEntity, TId>(TId id) where TEntity : EntityObject<TId>
}

public abstract class EntityObject<TId>
{
    public IdT id { get; set; }
}

public class Foo : EntityObject<Guid> {} 

You want to do:

IRepository repository = GetRepositoryFromSomewhere();
Foo foo = repository.Get<Foo>(someGuid);

Whereas currently you have to do:

Foo foo = repository.Get<Foo, Guid>(someGuid);

Yes, the compiler is making it very slightly harder for you than necessary. A whole 6 extra characters, for the sake of keeping the language simpler and the rules of type inference easier to understand.

Basically type inference is an all or nothing affair - either all type parameters are inferred or none of them is. That keeps it simple as you don't need to work out which ones are being specified and which aren't. That's part of the problem, and the other part is that you can only express constraints on the type parameters of the method - you can't have:

class Repository<TEntity>
{
    TEntity Get<TId>(TId id) where TEntity : EntityObject<TId>
}

because that's constraining TEntity, not TId. Again, this sort of thing makes type inference simpler.

Now you could potentially write:

Foo foo = repository.Get(someGuid).For<Foo>();

with an appropriate Get method and an extra interface. I think I'd personally prefer to just use Get<Foo, Guid> though.

like image 168
Jon Skeet Avatar answered Sep 19 '22 11:09

Jon Skeet