0

Thank you in advance. Here is my code:

public class ApplicationUser : IdentityUser
{
    public ApplicationUser()
    {

    }
}

public class AppUserManager : UserManager<ApplicationUser>
{
...
}

public interface IOwinManager
{
    UserManager<IdentityUser> UserManager { get; }
}

Why is this not working?

public class OwinManager : IOwinManager
{        
    public UserManager<IdentityUser> UserManager
    {
        get { return new AppUserManager(); }
    }
}

Since ApplicationUser inherits from IdentityUser and AppUserManager from UserManager, why is the combined generic not accepted? Thanks!

  • Is `UserManager<>` a class, not an interface? Just because "an `ApplicationUser` ***is an*** `IdentityUser`" does not imply that "a `UserManager` ***is a*** `UserManager`". Unless the type `UserManager<>` has been declared _covariant_ in its type argument. In C# in its current version, only `interface` types and `delegate` types can be declared covariant (`out` modifier on type parameter). `class` types and `struct` types cannot. – Jeppe Stig Nielsen Dec 16 '15 at 12:32

2 Answers2

4

Both contravariance and covariance on generic type parameters for classes isn't supported.

Simplifying your issue:

// Compiler error!
UserManager<IdentityUser> userManager = new AppUserManager();
  • AppUserManager inherits UserManager<ApplicationUser>.
  • Thus, you're trying to set an UserManager<ApplicationUser>-derived reference on a UserManager<IdentityUser> reference. This is the problem! They're different types.

OP said...:

which, essentially means, I can't use concrete classes and their generics in an interface and expect them to be implemented by their children?

Interfaces support variance. Thus, you can design your interface as follows:

public interface IOwinManager<out TUser, out TManager>
    where TUser : IdentityUser 
    where TManager : UserManager<TUser>
{
    TManager UserManager { get; }
}

...and once you've implemented this interface, your implementation will declare a property of the concrete TManager type.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
3

Matias's answer is good; I thought I'd add a bit more context. Let's again simplify your example:

class Animal {} // IdentityUser
class Tiger : Animal {} // ApplicationUser    
class Giraffe : Animal {} // some other kind of user
class Cage<T> where T : Animal {} // UserManager
class SpecialTigerCage : Cage<Tiger> {} // AppUserManager

The question now is "why is this conversion illegal?"

Cage<Animal> cage = new SpecialTigerCage();

It should be obvious now why this is illegal. You can put a giraffe into a cage that can contain animals, but if you put a giraffe into a special tiger cage, the giraffe is not going to be very happy about it.

The type system cannot prove to its satisfaction that you're not going to put a giraffe into that tiger cage, so it disallows the conversion.

As others pointed out, C# does support this sort of covariant conversion on interfaces and delegates, where it can prove that you're not going to put a giraffe into a tiger cage. IEnumerable<T> for example is covariant. A sequence of tigers may be used where a sequence of animals is needed because IEnumerable<T> provably has no method that can insert a giraffe into the sequence.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
  • Thank you - this does make it a bit clearer, but I am still confused. Perhaps I am confusing myself, because I expect that - should I then add a giraffe in a cage tigercage implementation, then if I explicitly sent my where T: Animal, I would only expect Animal methods to be called. So, I suppose that -is- possible, provided I explicitly declare T to be of type animal. Like you did? That should be legal, if it upcasts everything as Animal? – Evangelos Aktoudianakis Dec 16 '15 at 14:44
  • @EvangelosAktoudianakis: Casting everything to Animal works fine when pulling elements out (I wanted an Animal and was given a Tiger) but not when putting elements in (The collection wanted a Tiger and I gave it an an Animal). It's easy enough to give your Cage two interfaces, an interface for putting animals in and an interface for pulling things out (e.g., IEnumerable, but you can define your own). If you want such casting to be handled by the type system, use interfaces in your function signatures. – Brian Dec 16 '15 at 15:03
  • @EvangelosAktoudianakis: The question is not which methods will be called on objects of compile time type T. You are correct that methods of Animal would be called, but that's not relevant. The problem is, suppose `Cage where T : Animal` has a method `AddAnimal(T)`. So `Cage` has a method `AddAnimal(Animal)`. But `SpecialTigerCage` only implements `AddAnimal(Tiger)`; it cannot fulfill all the duties required of a `Cage`, so it may not be used as one. – Eric Lippert Dec 16 '15 at 17:48