简体   繁体   English

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

[英]Can I force a subclass to declare a constant?

I want to force subclasses to define a constant value. 我想强制子类定义一个常量值。

Like 喜欢

const string SomeConstantEverySubclassMustDefine = "abc";

I need that because I need to have it tied to the Type, rather than to the instance and you can't override static Methods/Properties iirc. 我需要它,因为我需要将它绑定到Type而不是实例,并且您不能覆盖静态方法/属性iirc。

I'd really like to have a compile-time check for those constants. 我真的想对这些常量进行编译时检查。

Let me explain in more detail: 让我更详细地解释一下:

Some classes in our Domain-Model are special, you can take certain actions for them, depending on the type. 我们的域模型中的某些类是特殊的,您可以根据类型为它们采取某些操作。 Thus the logic is tied to the type. 因此逻辑与类型有关。 The action to be taken requires a string tied to the type. 要采取的操作需要绑定到类型的字符串。 I sure could create an instance everytime as a workaround and declare an abstract property, but that's not what I want. 我确实可以每次创建一个实例作为变通方法并声明一个抽象属性,但这不是我想要的。 I want to enforce the declaration of the string at compile-time, just to be sure. 我想在编译时强制执行字符串的声明,只是为了确定。

No, you can't. 不,你不能。 I would suggest you make your base class abstract, with an abstract property which you can fetch when you want. 我建议你使用抽象属性使你的基类抽象,你可以在需要时获取它。 Each child class can then implement the property just by returning a constant if it wants. 然后,每个子类只需要返回一个常量即可实现该属性。 The downside is that you can't use this within static methods in the base class - but those aren't associated with the child classes anyway. 缺点是您不能在基类中的静态方法中使用它 - 但这些方法与子类无关。

(It also allows child classes to customise the property per instance as well, if necessary... but that's rarely an actual problem.) (它还允许子类在每个实例中自定义属性,如果需要的话......但这很少是实际问题。)

If this doesn't do enough for you, you might want to consider a parallel type hierarchy. 如果这对您不够,您可能需要考虑并行类型层次结构。 Basically polymorphism simply doesn't happen in a type-specific way in .NET; 基本上,多态性在.NET中不会以特定于类型的方式发生; only in an instance-specific way. 仅以特定于实例的方式。

If you still want to do this and fetch it with reflection, I suggest you just write unit tests to ensure that the relevant constants are defined. 如果您仍然希望这样做并使用反射获取它,我建议您只编写单元测试以确保定义相关常量。 When you get beyond what the type system can describe, that's often the best you can do. 当你超越类型系统可以描述的范围时,这通常是你能做的最好的事情。

Make a abstract property with only a get . 使用get一个abstract property That's what I think you could do to enforce a class has a value. 这就是我认为你可以做的强制一个类有价值。 Then you can just return a constant in the property. 然后你可以在属性中返回一个常量。

EG: 例如:

Base Class: 基类:

public abstract string MyConst { get; }

Then in the Derived Class 然后在派生类中

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

Here is how I made mine work. 这就是我如何使我的工作。 I used Attribute as others have suggested. 我使用了其他人建议的属性。

public class ObjectAttribute : Attribute
{
    public int ObjectSize { get; set; }
    public ObjectAttribute(int objectSize)
    {
        this.ObjectSize = objectSize;
    }
}
public abstract class BaseObject
{
    public static int GetObjectSize<T>() 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; }
}

If you would like instance access to the attribute just add it to the base abstract class. 如果您希望实例访问该属性,只需将其添加到基本抽象类。

public abstract class BaseObject
{
    public static int GetObjectSize<T>() 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;
        }
    }
}

Usage of the constants 使用常量

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

New idea 新想法

Here's a sort of weird idea: instead of using inheritance directly, you create a separate class to provide a constant value for every type deriving from some type T . 这是一个奇怪的想法:不是直接使用继承,而是创建一个单独的类,为从某个类型T派生的每个类型提供一个常量值。 The constructor for this type uses reflection to verify that every derived type has indeed been supplied a value. 类型的构造函数使用反射来验证是否确实为每个派生类型提供了值。

public abstract class Constant<T, TConstant>
{
    private Dictionary<Type, TConstant> _constants;

    protected Constant()
    {
        _constants = new Dictionary<Type, TConstant>();

        // Here any class deriving from Constant<T, TConstant>
        // 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<U>(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<U>() where U : T
    {
        return _constants[typeof(U)];
    }
}

Basic example: 基本示例:

public class BaseType
{
    public static Constant<BaseType, string> Description { get; private set; }

    static BaseType()
    {
        Description = new BaseTypeDescription();
    }
}

public class DerivedType : BaseType
{ }

internal sealed class BaseTypeDescription : Constant<BaseType, string>
{
    public BaseTypeDescription() : base()
    { }

    protected override DefineConstants()
    {
        DefineConstant<BaseType>("A base type");
        DefineConstant<DerivedType>("A derived type");
    }
}

Now I have code that allows me to do this: 现在我有了允许我这样做的代码:

var description = BaseType.Description;

// returns "A base type"
string baseTypeDescription = description.GetValue<BaseType>();

// returns "A derived type"
string derivedTypeDescription = description.GetValue<DerivedType>();

Original answer 原始答案

You may not like it, but the closest way to accomplish this is by declaring an abstract read-only (no set ) property. 您可能不喜欢它,但最接近的方法是声明一个抽象的只读(无set )属性。

If you've got an instance of your subclass, then this can work just as well as a constant, even though it is technically instance-level (it will just be the same for all instances of the given class). 如果你有一个子类的实例,那么这可以和常量一样工作,即使它在技术上是实例级的(它对于给定类的所有实例都是一样的)。

Consider, for instance, IList.IsReadOnly . 例如,考虑IList.IsReadOnly In most cases this is actually a property that tells you about the underlying class implementation, as opposed to any state specific to a particular instance. 在大多数情况下,这实际上是一个告诉您底层类实现的属性,而不是特定于特定实例的任何状态。 (It may be an interface member as opposed to an abstract class member, but it's the same idea.) (它可能是一个接口成员而不是抽象类成员,但它的想法是一样的。)

If you are trying to access it statically, well... then you're out of luck. 如果你试图静态访问它,那么......那你运气不好。 But in this case I fail to see how you'd obtain the value without using reflection anyway. 但在这种情况下,我无法看到你如何在不使用反射的情况下获得价值。 Maybe that's your intention; 也许那是你的意图; I don't know. 我不知道。

You could have a static method in the base class called, for instance "Register", that is passed a Type and a constant value, with the intention being that it is called by the class constructors of the subtypes. 您可以在基类中调用一个静态方法,例如“Register”,它传递一个Type和一个常量值,意图是它由子类的类构造函数调用。 Then, add a check in all of your base class constructors that the object being constructed is of a registered type. 然后,在所有基类构造函数中添加一个检查,表明正在构造的对象是已注册的类型。

abstract class Base
{
    private static Dictionary<Type, string> _registry = new Dictionary<Type, string>();

    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()
    {
    }
}

And then elsewhere, you can construct GoodSubtype instances, but trying to construct a Badsubtype gets an exception. 然后在其他地方,您可以构建GoodSubtype实例,但尝试构造Badsubtype会获得异常。 I think a runtime error at construction is the soonest you can get an error with this type of scheme. 我认为构造中的运行时错误是最快的,你可以通过这种类型的方案得到错误。

(You'd want to use ConcurrentDictionary for your registry if threading is involved) (如果涉及线程,您希望将ConcurrentDictionary用于您的注册表)

There's one other method that hasn't been covered and it uses the new modifier to hide consts values in the base class. 还有一个尚未涵盖的方法,它使用new修饰符隐藏基类中的consts值。 In a way, it's similar to Nap's solution , but doesn't allow per-instance access and therefore doesn't allow for polymorphic access within the base class. 在某种程度上,它类似于Nap的解决方案 ,但不允许每个实例访问,因此不允许在基类内进行多态访问。 This solution is only useful if you want to have constant value defined but wish to have the option of changing it to different values in different subclasses. 此解决方案仅在您希望定义常量值但希望可以选择将其更改为不同子类中的不同值时才有用。

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
{
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM