I am trying to use generic constraints to only allow a generic function to be called on the parent types of another type.
Example:
public class SomeClass<Derived>
where Derived : class
{
public void call<Parent>()
where Parent : class
{
ParenthoodChecker<Derived, Parent> checker =
new ParenthoodChecker<Derived, Parent>();
}
}
public class ParenthoodChecker<Derived, Parent>
where Parent : class
where Derived : Parent
{
public ParenthoodChecker()
{
}
}
Currently, I'm getting the following error message:
Error CS0311: The type 'Derived' cannot be used as type parameter 'Derived' in the generic type or method 'ParenthoodChecker'. There is no implicit reference conversion from 'Derived' to 'Parent'. (CS0311)
Is there some way to enforce such a thing compile time? I'd prefer not checking during runtime and I feel like the compiler should be able to deduce this.
Most answers give a partial answer so I'll compile all the answers and reasons why they may not work for specific cases here.
Option 1 : reversing caller & callee (m.rogalski's comment)
public class SomeClass<Parent> //instead of derived
where Parent : class
{
public void call<Derived>()
where Derived : class, Parent
{
ParentChecker<Derived, Parent> checker = new ParentChecker<Derived, Parent>();
}
}
This is the closest to a complete compile time check as you can get of course but it might not be feasible depending on the system design (which was my case).
Option 2 : using intermediate template type (Zoran Horvat's Answer)
public class SomeClass<Derived>
where Derived : class
{
public void call<Parent, NewDerived>()
where Parent : class
where NewDerived: Derived, Parent
{
ParentChecker<NewDerived, Parent> checker =
new ParentChecker<NewDerived, Parent>();
}
}
This works so long as you use it with correct input (which will be most cases) but the reason to use compile time checking is mostly to avoid having miss use of something so I don't feel it's a complete answer. The usability is weaker than ideal (you need to specify both types at the moment of the call) and it has potential counter examples such as :
public interface GrandP_A {}
public interface GrandP_B {}
public class Parent : GrandP_A {}
public class Child : Parent, GrandP_B {}
SomeClass<Parent> instance = new SomeClass<Parent>();
instance.call<GrandP_B, Child>();//this compiles
The goal of SomeClass is to check, that SomeClass's generic type parameter is derived from the first generic type of the call function. In this case GrandP_B. However, GrandP_B is not a parent class to Parent but the call still compiles. So again, this only works when you use it well (passing Parent as the second generic type of .call)
Option 3 : Runtime checking
I had to compromise and use Runtime Checking instead. This is obviously not the way to go as a compile time solution but it's the only one that allowed the specific design I had in mind. I'll still mention it here as a partial answer in case it helps someone in the future. For more info, check this other answer on that specific question
public class SomeClass<Derived>
where Derived : class
{
public void call<Parent>()
where Parent : class
{
if(typeof(Derived).IsSubclassOf(typeof(Parent)))
{
//do your stuff
}
else
{
throw new Exception("Must be called wih parent classes only!");
}
}
}