Вы можете сделать ограничение более жестким. Все перечисления реализуют следующие интерфейсы IFormattable
, IConvertible
и IComparable
. Это в значительной степени ограничит вас примитивами, перечислениями и классами, которые ведут себя как они.
В качестве альтернативы, если вы действительно хотите создать метод или класс, привязываемый только к перечислениям, вы можете сделать следующее:
public abstract class InvokerHelper<T> where T : class
{
public abstract R Invoke<R>(R value) where R : struct,T;
}
public class EnumInvoker : InvokerHelper<Enum>
{
public override R Invoke<R>(R value)
{
return (R)WrappedMethod(value);
}
}
Это неуклюжий и не может быть использован для методов расширения, но вы могли бы сделать это объект-одиночка, а затем иметь только общие методы.
Кроме того, вы можете написать свой код в C++/CLI
или Reflection.Emit
, что позволяет создавать классы, которые могут иметь ограничение where T:struct,Enum
На самом деле кажется, что вы просто хотите взять общий вызов метода T, который принимает перечисление и возвращает его как T правильно?
Тогда будет работать следующее.
public static T WrappedMethodInvoker<T>(T value) where T:struct,IComparable,IFormattable,IConvertible
{
Enum e;
if((e = value as Enum) == null)
throw new ArgumentException("value must be an Enum")
object res = WrappedMethod(e);
return (T)res;
}
Это немного проще. Во-первых, мы знаем, что T - это Enum
, вы можете использовать оператор as
, чтобы попытаться привести к типу NULL (ссылочный тип или структура NULL), что, безусловно, Enum
. Оттуда мы можем использовать ковариацию возвращаемых типов, WrappedMethod
возвращает Enum
, что означает, что мы можем сохранить его в объекте. Наконец, когда у вас есть объект в универсальном методе, вам синтаксически разрешено привести его к T. Он может завершиться с ошибкой, но мы точно знаем, что метод возвращает Enum того же типа.
Существуют некоторые издержки, связанные с тем, что вы всегда занимаетесь боксом и распаковкой. Является ли обернутый метод универсальным?
Я отредактировал ответ, чтобы показать первую технику, исправленную в вашем примере. Он чрезвычайно прост и безопасен, но для выполнения действий всегда требуется EnumInvoker.
К сожалению, оба ответа должны работать примерно одинаково, так как вызов WrappedMethod будет иметь значение, если его аргумент - Enum. Единственное преимущество первого метода состоит в том, что он строго типизирован, но общие виртуальные методы - самые медленные методы для вызова, насколько мне известно. Поэтому обход проверки типа может не стоить затрат на более простой вызов.