2

I'm trying to call a method with a definition similar to the following (simplified to avoid confusion):

public static void Register<T>(T value) where T : BaseClass, IInterface

This works fine so long as I have a class instance that defines both of those values. The problem occurs when I pass a `BaseClass' into a method and then try to use that instance in the above declaration. For example:

public class MyClass
{
    public MyClass(BaseClass value)
    {
        Register(value);
    }
}

I can pass and instance of a class that implements both BaseClass and IInterface into the constructor, but when I try to use that value in the Register method I get a compilation error stating:

The type 'BaseClass' cannot be used as type parameter 'T' in the generic type or method 'Register(T)'. There is no implicit reference conversion from 'BaseClass' to 'IInterface'.

If I change the type in the constructor like so:

public class MyClass
{
    public MyClass(IInterface value)
    {
        Register(value);
    }
}

I get an error stating:

The type 'IInterface' cannot be used as type parameter 'T' in the generic type or method 'Register(T)'. There is no implicit reference conversion from 'IInterface' to 'BaseClass'.

This seems like a bit of a catch-22. Is there a way that I can define the parameter to indicate that it must implement both BaseClass and IInterface?

2 Answers 2

7

As I was writing the question I came up with the answer and thought I would post it instead of deleting the question.

I just need to redefine the class:

public class MyClass<T> where T : BaseClass, IInterface
{
    public MyClass(T value)
    {
        Register(value);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

The solution given by Matt is an easy answer for situations where it is not necessary to store the passed-in object in a field or collection. If you need to persist the passed-in object, things get much harder. It's easy for a SomeClass<T> where T meets multiple constraints, to store items of class T and pass them as generics to routines with such constraints, but if a class has a method SomeMethod<TParam>(TParam thing) where TParam:IFoo,BaseBar, it won't have any field of type TParam. It could store thing into a field of type IFoo or BaseBar, but unless BaseBar implements IFoo, or all passed-in instances are going to derive from one particular BaseBar derivative which implements IFoo, there's no way to specify that a field's type will meet both constraints (if every instance does derive from one particular BaseBar derivative which implements IFoo, one could simply use that type as a single constraint, or for that matter not bother with generics at all—just use that as the parameter type).

There are ways of getting around these issues, either using Reflection, an interface pattern I call ISelf<T>, or some tricky nested callback interfaces. In some cases, though, it may be better to provide alternatives to methods that take double-constrained parameters (have the methods take a parameter of one constraint type, cast it to the other type, and accept the lack of compile-time safety).

1 Comment

This is the path that I was originally trying to go down. Glad to know it was a hard path and I had an easier path within my reach.

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.