0

Consider the following scenario:

public interface IEntity<TKey>
{
    TKey { get; set; }
}

public interface IRepository<TEntity, TKey>
      where TEntity : IEntity<TKey>
{
    void Store(TEntity entity);
    void Delete(TKey key);
}

Why do I need to expliclty add TKey as a generic argument to IRepository ?

Can't the compiler deduce or infer it from TEntity's type?

I'd like to achieve something like this:

public interface IRepository<TEntity>
    where TEntity : IEntity<TKey>
{
     void Store(TEntity entity);
     void Delete(TKey key);
}

It's not like TKey is only known at runtime:

IRepository<User> userRepo = new ConcreteRepository<User>();

Where User implements IEntity<string>

Matias Cicero
  • 25,439
  • 13
  • 82
  • 154
  • The compiler follows a specification. The specification requires the type parameter and it arguably does this for good reason as it avoids ambiguity and extra/complicated rules: consider `IEnumerable` and `IEnumerable` which are both separate types. The end of the question is confusing as TKey is the name given to a type-domain restriction, separate from the 'runtime' .. – user2864740 Sep 25 '16 at 19:52
  • It's not relevant whether the compiler could or couldn't infer it - it doesn't, and that's it. It's annoying, yes, but that's just the way it is. The CLR spec requires every generic type argument to be defined. F# has much better type inference than C#, and it still must do this (the main difference is that it will infer the correct type when you call a method that just takes `TEntity` :)). – Luaan Sep 25 '16 at 20:26

2 Answers2

1

In your example:

public interface IRepository<TEntity>
where TEntity : IEntity<TKey> {
   void Store(TEntity entity);
   void Delete(TKey key);
}

TKey is an undefined type parameter. You could say where TEntity : IEntity<string>, since string is a defined type. But if you're going to use a type parameter, you'll need to define it first.

Notice that the compiler has no idea what TKey is here. Is it a type? Is it a generic type parameter?

You might be able to do something like this, but it would no longer be strongly typed on TEntity. That may or may not be acceptable:

public interface IRepository<TKey> {
   void Store(IEntity<TKey> entity);
   void Delete(TKey key);
}
Dave Cousineau
  • 12,154
  • 8
  • 64
  • 80
-1

Because the C# spec requires that for constructor parameters. The canonical answer on this matter is here Why can't the C# constructor infer type?

Worth noting is a couple of things: we almost got it in C#6. You can hack around it by using a Static method that simply calls your constructor. For instance

public class Entity<TKey>
{
    public Entity(TKey k)
    {

    }

}

public static class Entity
{
    public static Entity<MyKey> Create<MyKey>(MyKey mk)
    {
        return new Entity<MyKey>(mk);
    }
}

I placed it on a non-generic type of the same name for convenience sake.

Community
  • 1
  • 1
jamesbascle
  • 854
  • 1
  • 10
  • 17