简体   繁体   中英

Passing unconstrained generic type parameter to a constrained method

I have too methods:

public TValueType? DoStuffWithValueType<TValueType>(int x, int y) 
   where TValueType: struct {}

public TRefType DoStuffWithRefType<TRefType>(int x, int y) 
   where TRefType: class {} 

How can i wrap them in a new third method?

The following is not compiling since i cannot persuade the compiler that T is in fact a struct when calling DoStuffWithValueType :

public T DoStuff<T>(int x, int y) {

   if(typeof(T).IsValueType)
   {
      return DoStuffWithValueType<T>(x, y);
   }

   return DoStuffWithRefType<T>(x, y);
}

I already tried overloading DoStuff , but this attempt failed since generic-constraints are not part of the method signature.
I also tried to get rid of the constraints, but i could not.

Any ideas? Thank you!

You can't, basically - you'd have to invoke the relevant methods with reflection, which is ugly.

Of course you can do this with dynamic typing, which hides the reflection from you:

public T DoStuff<T>(int x, int y) {
   dynamic d = this;
   if(typeof(T).IsValueType)
   {
       return d.DoStuffWithValueType<T>(x, y);
   }    
   return d.DoStuffWithRefType<T>(x, y);
}

You may think that's cleaner than doing it manually with reflection - or you may not :)

There's no way that I'm aware of to make the compiler "trust" a type argument where it wouldn't normally do so.

Aside from Jon Skeet's use of dynamic , the cleanest way I can think of, minimising the required reflection, and so keeping as much verified by the compiler as possible, is to call the method through a helper class.

abstract class DoStuffHelper<T> {
    public abstract T DoStuff(int x, int y);
}

class DoStuffWithValueTypeHelper<T> : DoStuffHelper<T> where T : struct {
    public override T DoStuff(int x, int y) {
        return DoStuffWithValueType<T>(x, y);
    }
}

class DoStuffWithRefTypeHelper<T> : DoStuffHelper<T> where T : class {
    public override T DoStuff(int x, int y) {
        return DoStuffWithRefType<T>(x, y);
    }
}

public T DoStuff<T>(int x, int y) {
   DoStuffHelper<T> helper;
   Type helperType;

   if(typeof(T).IsValueType)
       helperType = typeof(DoStuffWithValueTypeHelper<>);
   else
       helperType = typeof(DoStuffWithRefTypeHelper<>);

   helperType = helperType.MakeGenericType(typeof(T));
   helper = (DoStuffHelper<T>)Activator.CreateInstance(helperType);

   return helper.DoStuff(x, y);
}

If appropriate for your situation, you can cache the helper classes in a Dictionary<Type, object> to avoid re-creating them every time.

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