我可以强制子类声明一个常量吗?

我想强制子类定义一个常量值。

喜欢

const string SomeConstantEverySubclassMustDefine = "abc"; 

我需要它,因为我需要将它绑定到Type而不是实例,并且您不能覆盖静态方法/属性iirc。

我真的想对这些常量进行编译时检查。

让我更详细地解释一下:

我们的域模型中的某些类是特殊的,您可以根据类型对它们采取某些操作。 因此逻辑与类型有关。 要采取的操作需要绑定到类型的字符串。 我确实可以每次创建一个实例作为变通方法并声明一个抽象属性,但这不是我想要的。 我想在编译时强制执行字符串的声明,只是为了确定。

不,你不能。 我建议你使用一个抽象属性使你的基类抽象,你可以在你想要的时候获取它。 然后,每个子类可以通过返回一个常量来实现该属性。 缺点是你不能在基类中的静态方法中使用它 – 但是那些与子类无关。

(它还允许子类在每个实例中自定义属性,如果需要的话……但这很少是实际问题。)

如果这对您不够,您可能需要考虑并行类型层次结构。 基本上,多态性在.NET中不会以特定于类型的方式发生; 仅以特定于实例的方式。

如果您仍然希望这样做并使用reflection获取它,我建议您只编写unit testing以确保定义相关常量。 当你超越类型系统可以描述的范围时,这通常是你能做到的最好的。

只使用get一个abstract property 。 我认为你可以做的就是强制执行一个有价值的东西。 然后你可以在属性中返回一个常量。

例如:

基类:

 public abstract string MyConst { get; } 

然后在派生类中

 public override string MyConst { get { return "constant"; } } 

这是我如何使我的工作。 我使用了其他人建议的属性。

 public class ObjectAttribute : Attribute { public int ObjectSize { get; set; } public ObjectAttribute(int objectSize) { this.ObjectSize = objectSize; } } public abstract class BaseObject { public static int GetObjectSize() where T : IPacket { ObjectAttribute[] attributes = (ObjectAttribute[])typeof(T).GetCustomAttributes(typeof(ObjectAttribute), false); return attributes.Length > 0 ? attributes[0].ObjectSize : 0; } } [ObjectAttribute(15)] public class AObject : BaseObject { public string Code { get; set; } public int Height { get; set; } } [ObjectAttribute(25)] public class BObject : BaseObject { public string Code { get; set; } public int Weight { get; set; } } 

如果您希望实例访问该属性,只需将其添加到基本抽象类。

 public abstract class BaseObject { public static int GetObjectSize() where T : IPacket { ObjectAttribute[] attributes = (ObjectAttribute[])typeof(T).GetCustomAttributes(typeof(ObjectAttribute), false); return attributes.Length > 0 ? attributes[0].ObjectSize : 0; } public int ObjectSize { get { ObjectAttribute[] attributes = (ObjectAttribute[])GetType().GetCustomAttributes(typeof(ObjectAttribute), false); return attributes.Length > 0 ? attributes[0].ObjectSize : 0; } } } 

常量的用法

 int constantValueA = AObject.GetObjectSize(); int constantValueB = BObject.GetObjectSize(); AObject aInstance = new AObject(); int instanceValueA = aInstance.ObjectSize; 

新想法

这是一个奇怪的想法:不是直接使用inheritance,而是创建一个单独的类,为从某个类型T派生的每个类型提供一个常量值。 类型的构造函数使用reflection来validation是否确实为每个派生类型提供了值。

 public abstract class Constant { private Dictionary _constants; protected Constant() { _constants = new Dictionary(); // Here any class deriving from Constant // should put a value in the dictionary for every type // deriving from T, using the DefineConstant method below. DefineConstants(); EnsureConstantsDefinedForAllTypes(); } protected abstract void DefineConstants(); protected void DefineConstant(TConstant constant) where U : T { _constants[typeof(U)] = constant; } private void EnsureConstantsDefinedForAllTypes() { Type baseType = typeof(T); // Here we discover all types deriving from T // and verify that each has a key present in the // dictionary. var appDomain = AppDomain.CurrentDomain; var assemblies = appDomain.GetAssemblies(); var types = assemblies .SelectMany(a => a.GetTypes()) .Where(t => baseType.IsAssignableFrom(t)); foreach (Type t in types) { if (!_constants.ContainsKey(t)) { throw new Exception( string.Format("No constant defined for type '{0}'.", t) ); } } } public TConstant GetValue() where U : T { return _constants[typeof(U)]; } } 

基本示例:

 public class BaseType { public static Constant Description { get; private set; } static BaseType() { Description = new BaseTypeDescription(); } } public class DerivedType : BaseType { } internal sealed class BaseTypeDescription : Constant { public BaseTypeDescription() : base() { } protected override DefineConstants() { DefineConstant("A base type"); DefineConstant("A derived type"); } } 

现在我有代码允许我这样做:

 var description = BaseType.Description; // returns "A base type" string baseTypeDescription = description.GetValue(); // returns "A derived type" string derivedTypeDescription = description.GetValue(); 

原始答案

您可能不喜欢它,但最接近的方法是声明一个抽象的只读(无set )属性。

如果你有一个你的子类的实例,那么这可以和常量一样工作,即使它在技术上是实例级的(它对于给定类的所有实例都是一样的)。

例如,考虑一下IList.IsReadOnly 。 在大多数情况下,这实际上是一个告诉您底层类实现的属性,而不是特定于特定实例的任何状态。 (它可能是一个接口成员而不是抽象类成员,但它的想法是一样的。)

如果你试图静态访问它,那么……那你运气不好。 但在这种情况下,我无法看到你如何在不使用reflection的情况下获得价值。 也许那是你的意图; 我不知道。

您可以在基类中调用一个静态方法,例如“Register”,它传递一个Type和一个常量值,意图是它由子类的类构造函数调用。 然后,在所有基类构造函数中添加一个检查,表明正在构造的对象是已注册的类型。

 abstract class Base { private static Dictionary _registry = new Dictionary(); protected static void Register(Type t, string constVal) { _registry.Add(t, constVal); } protected Base() { if(!_registry.ContainsKey(this.GetType())) throw new NotSupportedException("Type must have a registered constant"); } public string TypeConstant { get { return _registry[this.GetType()]; } } } class GoodSubtype : Base { static GoodSubtype() { Base.Register(typeof(GoodSubtype), "Good"); } public GoodSubtype() : base() { } } class Badsubtype : Base { public Badsubtype() : base() { } } 

然后在其他地方,您可以构建GoodSubtype实例,但尝试构造Badsubtype会获得exception。 我认为构造中的运行时错误是最快的,你可以通过这种类型的方案得到错误。

(如果涉及线程,您希望将ConcurrentDictionary用于您的注册表)

还有一个尚未涵盖的方法,它使用new修饰符隐藏基类中的consts值。 在某种程度上,它类似于Nap的解决方案 ,但不允许每个实例访问,因此不允许在基类内进行多态访问。 此解决方案仅在您希望定义常量值但希望可以选择将其更改为不同子类中的不同值时才有用。

 static void Main(string[] args) { Console.WriteLine("BaseClass.MyConst = {0}, ClassA.MyConst = {1}, ClassB.MyConst = {2}", BaseClass.MyConst, ClassA.MyConst, ClassB.MyConst); Console.ReadKey(); } class BaseClass { public const int MyConst = 1; } class ClassA : BaseClass { public new const int MyConst = 2; } class ClassB : BaseClass { }