I've searched and have found answers that make sense when you have access to change an existing interface definition. In my case, I do not have that ability. I'm trying to create a new object with a generic that has a constraint from a generic method that does not. The following code illustrates what I'm trying to do (I can do reflection or anything necessary in MyImplementedClass, but the others are more fixed):
// This existing interface has no constraint (can't change)
public interface IExistingInterface
{
void ExistingMethod<T>();
}
public class MyImplementedClass : IExistingInterface
{
public void ExistingMethod<T>()
{
var howToDoThis = new ExistingClass<T>(); // Gives error that T must be a reference type
}
}
// This existing class has the following constraint (can't change)
public class ExistingClass<T> where T : class
{
}
One possibility would be to publicly implement a method on MyImplementedClass
with the required constraint, then implement IExistingInterface.ExistingMethod<T>()
explicitly, making it call the public method via MethodInfo.MakeGenericMethod(typeof(T)).Invoke()
:
public class MyImplementedClass : IExistingInterface
{
void IExistingInterface.ExistingMethod<T>()
{
if (typeof(T).IsValueType)
{
// TODO: decide what to do when T is a value type
throw new ArgumentException($"Invalid type {nameof(T)}");
}
else
{
var method = typeof(MyImplementedClass).GetMethod(nameof(MyImplementedClass.ExistingMethod));
method.MakeGenericMethod(typeof(T)).Invoke(this, Array.Empty<object>());
}
}
public void ExistingMethod<T>() where T : class
{
var howToDoThis = new ExistingClass<T>();
Console.WriteLine($"Created {howToDoThis}");
}
}
The advantages and disadvantages of this approach are:
Inside the properly constrained public method you will be able to declare variables as ExistingClass<T>
rather than just dynamic
or object
.
Trying to directly call the properly constrained public method with a value type will result in a compile error. Trying to call the unconstrained directly implemented interface method will result in a runtime argument exception (or whatever else you choose to do in the //TODO
).
There will be no reflection penalty when the properly constrained method is called directly.
By explicitly implementing the interface method, one nudges (but does not require) consumers of MyImplementedClass
to call the properly constrained public method which provides for compile-time error checking rather than runtime error checking.
However MethodInfo.MakeGenericMethod()
is not guaranteed to work in Native AOT apps.
Demo fiddle here.