Return 从抽象泛型方法派生 class 的实例

Return instance of deriving class from abstract generic method

我想在这里做的事情有点难以描述。我目前的需求要求我有一个可以实现接口的枚举类型。虽然这不是最漂亮的解决方案,但这是我想出的;

public class EnumClass<T> where T : Enum
{
    public T Value { get; }
    public string Name { get; }

    public EnumClass(T enumValue)
    {
        Value = enumValue;
        Name = Enum.GetName(typeof(T), enumValue);
    }

    public static EnumClass<T> Parse(string name)
    {
        return new EnumClass<T>((T)Enum.Parse(typeof(T), name));
    }
}

这是一个示例实现:

public class AnimalTypes : EnumClass<AnimalTypesEnum>, IMyEnumInterface
{
    public AnimalTypes (AnimalTypesEnum value) : base(value) { }
}

public enum AnimalTypesEnum
{
    [Description("Cat")]
    CAT,
    [Description("Dog")]
    DOG,
    [Description("Horse")]
    HORSE,
    [Description("Bear")]
    BEAR
}

当我在继承者上静态调用 Parse 时,我必须手动将结果从基类型转换回继承者类型,因为 Parse return 是一个泛型 EnumClass<T>对象。

例如

AnimalTypes dog = (AnimalTypes)AnimalTypes.Parse("DOG");

我的问题本质上是,是否有任何方法可以编写 Parse 使其 return 成为继承者的类型,而不是基础 class?我也希望能够标记 EnumClass<T> 抽象,但如果我现在尝试这样做,编译器将不会编译 Parse,说明我无法创建类型 [=17= 的抽象实例] return.

您需要添加另一个类型参数,以便参数化 Parse 的 return 值类型并启用正在创建的 derived/inherited 类型。

Usage:
var bear = EnumClass<AnimalTypesEnum>.Parse<AnimalTypes>("BEAR");

//AnimalTypesEnum unchanged
//AnimalTypes unchanged

public abstract class EnumClass<TEnum> where TEnum : Enum
{
    public TEnum      Value { get; }
    public string Name  { get; }

    protected EnumClass(TEnum enumValue)
    {
        Value = enumValue;
        Name  = Enum.GetName(typeof(TEnum), enumValue);
    }

    public static TEnumClass Parse<TEnumClass>(string name)
            where TEnumClass : EnumClass<TEnum>
    {
        //TODO: try/catch
        /* Contract: the derived class must have a public constructor
                     that takes 1 arg of its enum type.
           Generic constraints don't support ctors with args, so we need reflection here... */
        return (TEnumClass)Activator.CreateInstance(
                typeof(TEnumClass), Enum.Parse(typeof(TEnum), name));
    }
}

您可以使用一种奇怪的递归模板模式,但它需要默认构造函数并且感觉很奇怪。通常情况下,如果事情变得如此复杂,值得询问是否可以重组您的需求以使其不那么复杂,但是很难知道所提供的详细信息是否可行。也就是说,这可能与您所要求的最接近。

无法指定方法 return 派生类型,但您可以使用泛型类型指定 return 类型。下面是 EnumClass,但已修改为采用两种通用类型。第一种类型和以前一样是枚举类型,但第二种类型用于指定派生类型(因此是模板的递归部分)。

    public abstract class EnumClass<T, TDerived>
        where T : Enum where TDerived : EnumClass<T, TDerived>, new()
    {
        protected EnumClass()
        {
        }

        protected EnumClass(T enumValue)
        {
            Value = enumValue;
        }

        private T _value = default(T);
        public T Value
        {
            get => _value;
            init => _value = value;
        }

        private string _name = null;
        public string Name
        {
            get
            {
                _name = _name ?? Enum.GetName(typeof(T), Value);
                return _name;
            }
        }

        public static TDerived Parse(string name)
        {
            var enumValue = (T)Enum.Parse(typeof(T), name);
            return new TDerived() {Value = enumValue};
        }
    }

然后,使用此 EnumClass 的派生类型将如下所示,其中第二个泛型类型递归地引用自身,这意味着 EnumClass 中的静态 Parse 方法将 return 类型 AnimalTypes.

    public class AnimalTypes : EnumClass<AnimalTypesEnum, AnimalTypes>
    {
        public AnimalTypes(): base()
        {
        }

        public AnimalTypes(AnimalTypesEnum value): base(value)
        {
        }
    }

在使用中,它看起来像这样

        //because we are required to have public default constructors, it's possible
        //to have a "default" AnimalTypes class that would be similar to constructing
        //a "new AnimalTypes(default(AnimalTypesEnum));"
        var defaultType = new AnimalTypes();
        //this will output "CAT, CAT"
        Console.WriteLine($"{defaultType.Value}, {defaultType.Name}");

        //Since we are using init, you can initialize the value using this format
        //instead of using the constructor
        var horseType = new AnimalTypes() {Value = AnimalTypesEnum.HORSE};
        //this will output "HORSE, HORSE"
        Console.WriteLine($"{horseType.Value}, {horseType.Name}");

        //normal constructor
        var dogType = new AnimalTypes(AnimalTypesEnum.DOG);
        //this will output "DOG, DOG"
        Console.WriteLine($"{dogType.Value}, {dogType.Name}");

        //static parser will return a type of AnimalTypes
        var bearType = AnimalTypes.Parse("BEAR");
        //this will output "BEAR, BEAR"
        Console.WriteLine($"{bearType.Value}, {bearType.Name}");