如何使用没有约束的类型参数调用具有泛型约束的方法?

How to call a method with a generic constraint using a type parameter without the constraint?

假设我有一个方法:

public void ExampleMethod<T>(T x) where T : struct // or new()
{
    // example usage, similar to what's actually happening
    var z = (T)Enum.Parse(typeof(T), privateFieldOfTypeString);

    // do something depending on values of x and z
    // possibly using other methods that require them being enums

    // or in case of the new() constraint:
    // var t = new T() and do something together with x
}

我想按如下方式使用它:

public void CallerMethod<S>(S y)
{
    if (typeof(S).IsEnum) // or some other invariant meaning that `S` is "new()able"
    {
        ExampleMethod(y); // won't compile
    }
}

因此,在 运行 期间,我知道 S 满足 ExampleMethod<T> 的约束条件。我知道可以使用反射来调用它,类似于:

this
    .GetType()
    .GetMethod(nameof(ExampleMethod<object>))
    .MakeGenericMethod(typeof(S))
    .Invoke(this, new object[] { y });

不反射可以吗?

注意: 这是来自真实示例的简化代码,显然我无法控制这些方法的签名,因此答案 "add the constraint to CallerMethod" 和"remove the constraint from ExampleMethod" 无效。

是的,整个事情都应该重新设计,这样整个问题就不会出现了。但在现实生活中,"the whole thing" 太大、太耦合且重写的风险太大。一些要求以意想不到的方式发生了变化 - 因此出现了明显的代码味道,我试图通过将其隐藏在一个看起来很讨厌的地方来尽量减少这种味道。

你可以使用 dynamic:

if (typeof(S).IsEnum)
{
    ExampleMethod((dynamic)y);
}

在您的特定情况下,您可以直接转换为 int。

public void CallerMethod<S>(S y)
{
    if (typeof(S).IsEnum)
    {
        ExampleMethod((int)y);
    }
}

如果枚举类型已知,一种可能性是转换为已知类型,尽管很冗长。

例如,作为起点,

public void CallerMethod<S>(S y) {
    if (typeof(S).IsEnum) {
        if (y is KnownEnum) {
            var z = (KnownEnum)Enum.Parse(typeof(S), y.ToString());
            ExampleMethod(z);
        }
        if (y is KnownEnum2) {
            var z = (KnownEnum2)Enum.Parse(typeof(S), y.ToString());
            ExampleMethod(z);
        }
        //...
    }
}

您可以利用运算符重载;定义 CallerMethod 的多个显式版本,它们都可以成功地对 ExampleMethod

进行后续调用
CallerMethod(Enum1 value) { ExampleMethod(value); }
CallerMethod(Enum2 value) { ExampleMethod(value); }
CallerMethod(Enum3 value) { ExampleMethod(value); }

等等

如果有大量不断增长的类型需要 CallerMethod 版本,您可以编写 T4 template 来生成包含所有实现的 partial class。