2

假设我有一个方法:

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 });

没有反射可以吗?

注意:这是来自真实示例的简化代码,显然我无法控制这些方法的签名,因此答案“将约束添加到CallerMethod”和“从约束中删除ExampleMethod”是无效的。

Yes, the whole thing should be redesigned so the whole problem wouldn't appear at all. But as often in real-life "the whole thing" is too big, too coupled and too risky to rewrite. Some requirements have changed in an unexpected way - hence the apparent code smell, which I'm trying to minimize by hiding it in a single nasty-looking place.

4

4 回答 4

3

You could use dynamic:

if (typeof(S).IsEnum)
{
    ExampleMethod((dynamic)y);
}
于 2018-11-23T09:44:30.337 回答
2

You could utilise operator overloading; define multiple explicit versions of CallerMethod that can all successfully make a follow on call to ExampleMethod

CallerMethod(Enum1 value) { ExampleMethod(value); }
CallerMethod(Enum2 value) { ExampleMethod(value); }
CallerMethod(Enum3 value) { ExampleMethod(value); }

etc.

If there are a large number of growing types that need a version of CallerMethod you could write a T4 template to generate a partial class with all implementations.

于 2018-11-23T14:50:48.277 回答
1

If the enum types are known, one possibility, though verbose, would be to convert to known types.

For example, as a starting point,

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);
        }
        //...
    }
}
于 2018-11-23T10:55:52.883 回答
-3

In your specific case you can just cast to an int.

public void CallerMethod<S>(S y)
{
    if (typeof(S).IsEnum)
    {
        ExampleMethod((int)y);
    }
}
于 2018-11-23T10:05:15.687 回答