1

I have the following class which implements an interface:

public class Category : IHierarchical<Category> {
    ...
}

I then have an inherited type called ProductCategory:

public class ProductCategory : Category {
    ...
}

Here's my IHierachical interface:

public interface IHierarchical<out T> where T : IHierarchical<T> {
    T Parent { get; }
    IEnumerable<T> Children { get; }
}

Finally I have the following extension method:

public static string MyExtension<T>(this T item) where T : IHierarchical<T> {
    ...
}

If I have an instance of type Category I can successfully call my extension method. However if my instance is of type ProductCategory I cannot call the extension method against it and instead I get the error:

The type 'ProductCategory' cannot be used as type parameter 'T' in the generic
type or method 'IHierarchicalExtensions.MyExtension<T>(T, string)'. There is no
implicit reference conversion from 'ProductCategory' to
'IHierarchical<ProductCategory>'.

I thought by making IHierachical covariant it would solve this issue. I'd appreciate it if someone could show me how this can be done. Thanks

3
  • That's probably because ProductCategory doesn't implement the interface, if you tried ProductCategory is IHierarchical, it'd give you false. If you make your child implement the interface as well, it'd work. Commented Oct 9, 2014 at 11:36
  • 1
    @artm Actually ProductCategory is IHierarchical<Category> which does not meet the restrain where T : IHierarchical<T>. Commented Oct 9, 2014 at 11:38
  • @juharr Didn't know that ProductCategory is IHierarchical<Category> with inheritanc. Commented Oct 9, 2014 at 11:39

4 Answers 4

2

The inferred type for T is ProductCategory and as the compiler error suggests, this does not implement IHierarchical<ProductCategory>. You can manually specify the type as Category however:

var pc = new ProductCategory();
pc.MyExtension<Category>();
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks. This looks cleaner than doing a cast to me.
2

I assume you have something like this:

Category c = new Category();
ProductCategory pc = new ProductCategory();

c.MyExtension();
pc.MyExtension();//Compiler error!

Your extension method allows T which means you can pass any type, your compile time type of pc is ProductCategory, so when you invoke MyExtension, T is of type ProductCategory.

What about the constraint where T : IHierarchical<T>?

It says that ProductCategory should be a IHierarchical<ProductCategory> clearly your ProductCategory is not. It is rather IHierarchical<Category> and so it doesn't compiles.

To fix it you may need a upcast to Category.

((Category)pc).MyExtension();

Comments

2

Your problem is that ProductCategory does not meet the restraint where T : IHierarchical<T> because ProductCategory is actually IHierarchical<Category> because it inherits that from Category. As for how to fix this is really up to what you are trying to accomplish. You can either losen up the restraint or change ProductCategory to implement IHierarchical<ProductCategory> (It can still inherit from Category) or cast your ProductCategory to a Category, or finally specify the type for T as Category when you call your extension method.

Comments

1

When you pass a ProductCategory item to MyExtension, T is inferred to be ProductCategory, so the constraint is ProductCategory : IHierarchical<ProductCategory>. It fails because ProductCategory only implements IHierarchical<Category>.

Covariance means that an IHierarchical<ProductCategory> object can be used where an IHierarchical<Category> is expected. What you are trying to do is the opposite. Thus you would need contravariance for this, but that is not the solution here (you can't have a T Parent property if T is contravariant).

To successfully use this code, you can specify the T in your call:

ProductCategory pc = ...;
pc.MyExtension<Category>();

This will succeed because the constraint becomes IHierarchical<Category> which pc satisfies.

Another solution is to use a non-generic interface, which the generic interface inherits, for the extension:

public interface IHierarchical {
    ...
}

public interface IHierarchical<out T> : IHierarchical where T : IHierarchical<T> {
    ...
}

public static string MyExtension(this IHierarchical item) {
    ...
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.