简体   繁体   中英

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

Let's say I have a method:

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
}

and I want to use it as follows:

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

So, during run-time I know that S satisfies the constraint for ExampleMethod<T> . I know it's possible to call it using reflection, something along the lines of:

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

Is it possible without reflection?

Note: this is a simplified code from a real-life example and obviously I have no control over the signatures of these methods, hence the answers "add the constraint to CallerMethod " and "remove the constraint from ExampleMethod " are invalid.

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.

You could use dynamic :

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

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.

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

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM