繁体   English   中英

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

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

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

喜欢

const string SomeConstantEverySubclassMustDefine = "abc";

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

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

让我更详细地解释一下:

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

不,你不能。 我建议你使用抽象属性使你的基类抽象,你可以在需要时获取它。 然后,每个子类只需要返回一个常量即可实现该属性。 缺点是您不能在基类中的静态方法中使用它 - 但这些方法与子类无关。

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

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

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

使用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<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; }
}

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

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

使用常量

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

新想法

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

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)];
    }
}

基本示例:

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");
    }
}

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

var description = BaseType.Description;

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

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

原始答案

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

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

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

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

您可以在基类中调用一个静态方法,例如“Register”,它传递一个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()
    {
    }
}

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

(如果涉及线程,您希望将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
{
}

暂无
暂无

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

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