C#: cast to generic interface with base type

Solution 1:

Maybe it helps you if I explain why this cast is forbidden: Assume that you have the following function

void myFunc(IValidator<BaseEntity> myValidator) {
    myValidator.IsValid(new BaseEntity());
}

This code would compile correctly. Nevertheless, if you passed an OrderValidator to this function, you would get a run-time exception, because OrderValidator.IsValid expects an Order, not a BaseEntity. Type safety would no longer be maintained if your cast were allowed.

EDIT: C# 4 allows for generic co- and contravariance, but this would not help in your case, since you use T as an input parameter. Thus, only casting to an IValidator<SomeSubtypeOfOrder> could be done in a type-safe way.

So, to be clear, you cannot cast OrderValidator to IValidator<BaseEntity> because your OrderValidator can only validate orders, not all kinds of BaseEntities. This, however, is what would be expected of an IValidator<BaseEntity>.

Solution 2:

The cast doesn't work because IValidator<Order> and IValidator<BaseEntity> are totally unrelated types. IValidator<Order> is not a subtype of IValidator<BaseEntity>, so they can't be cast.

C# does support multiple interface inheritance, so the simplest way to handle this is to make your order validator inherit from an interface for both validator types, that way it you will be able to cast it to either interface as required. Obviously this means you will have to implement both interfaces and specify how to handle the base when a BaseEntity provided doesn't match the type the validator is for.

Something like this:

public class OrderValidator : IValidator<Order>, IValidator<BaseEntity>
{
    public bool IsValid(Order obj)
    {
        // do validation
        // ...
        return true;
    }

    public bool IsValid(BaseEntity obj)
    {
        Order orderToValidate = obj as Order;
        if (orderToValidate != null)
        {
            return IsValid(orderToValidate);
        }
        else
        {
            // Eiter do this:
            throw new InvalidOperationException("This is an order validator so you can't validate objects that aren't orders");
            // Or this:
            return false;
            // Depending on what it is you are trying to achive.
        }
    }
}

This relates to what Heinzi says about not being able to cast because an IValidator<BaseEntity> needs to be able to validate BaseEntities, which your current OrderValidator can't do. By adding this multiple interface you explicitly define the behaviour for validating BaseEntities (by either explicitly ignoring it or causing an exception) so the cast becomes possible.