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

Fal*_*con 28 c#

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

喜欢

const string SomeConstantEverySubclassMustDefine = "abc";
Run Code Online (Sandbox Code Playgroud)

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

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

让我更详细地解释一下:

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

Jon*_*eet 20

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

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

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

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


Bob*_*mer 9

做一个abstract property只有一个get.这就是我认为你可以做的强制一个类有价值.然后你可以在属性中返回一个常量.

例如:

基类:

public abstract string MyConst { get; }
Run Code Online (Sandbox Code Playgroud)

然后在派生类中

public override string MyConst {
    get { return "constant"; }
}
Run Code Online (Sandbox Code Playgroud)

  • 它有效,除了接受答案中评论的情况:“你不能在静态方法中使用它”。因此,您也无法直接从类访问该字符串的值,您需要一个实例化对象。 (2认同)

Nap*_*Nap 8

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

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; }
}
Run Code Online (Sandbox Code Playgroud)

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

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;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用常量

int constantValueA = AObject.GetObjectSize<AObject>();
int constantValueB = BObject.GetObjectSize<BObject>();
AObject aInstance = new AObject();
int instanceValueA = aInstance.ObjectSize;
Run Code Online (Sandbox Code Playgroud)

  • 这不强制任何东西......你仍然依赖于子类继承者知道他们需要向类添加自定义属性. (2认同)

Dan*_*Tao 6

新想法

这是一个奇怪的想法:不是直接使用继承,而是创建一个单独的类,为从某种类型派生的每个类型提供常量值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)];
    }
}
Run Code Online (Sandbox Code Playgroud)

基本示例:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

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

var description = BaseType.Description;

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

// returns "A derived type"
string derivedTypeDescription = description.GetValue<DerivedType>();
Run Code Online (Sandbox Code Playgroud)

原始答案

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

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

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

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