What I want to do here is a bit hard to describe. My current needs require that I have an enum type that can implement an interface. While not the prettiest solution, this is what I came up with;
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));
}
}
Here is an example implementation:
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
}
When I call Parse
statically on an inheritor, I have to manually cast the result back to the inheritor type from the base type, since Parse
returns a generic EnumClass<T>
object.
ex.
AnimalTypes dog = (AnimalTypes)AnimalTypes.Parse("DOG");
My question essentially is, is there any way to write Parse
such that it returns the type of the inheritor, and not the base class? I'd also like to be able to mark EnumClass<T>
abstract, but if I try doing so now, the compiler will not compile Parse
, stating that I cannot create an abstract instance of type EnumClass<T>
with which to return.
You can use a curiously recursive template pattern, but it requires default constructors and feels odd. Normally if things get this convoluted it's worth asking if your requirements can be restructured so that it's not so complicated, but it's hard to know if that's possible with the details given. That said, this may be as close to what you are asking for that you can get.
There isn't a way to specify that a method return the derived type, but you can specify the return type using a generic type. Below is the EnumClass, but modified to take two generic types. The first type is the enum type like before, but the second is for specifying the derived type (hence the recursive part of the template).
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};
}
}
Then, a derived type using this EnumClass would look like this, where the second generic type recursively refers to itself, which means that the static Parse method in the EnumClass will return a type AnimalTypes
.
public class AnimalTypes : EnumClass<AnimalTypesEnum, AnimalTypes>
{
public AnimalTypes(): base()
{
}
public AnimalTypes(AnimalTypesEnum value): base(value)
{
}
}
In use, it would look like this
//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}");