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