Подтвердить что ты не робот

Могу ли я заставить подкласс объявлять константу?

Я хочу заставить подклассы определять постоянное значение.

Как

const string SomeConstantEverySubclassMustDefine = "abc";

Мне нужно это, потому что мне нужно привязать его к типу, а не к экземпляру, и вы не можете переопределить статические методы/свойства iirc.

Мне бы очень хотелось, чтобы эти константы проверяли время компиляции.

Позвольте мне объяснить более подробно:

Некоторые классы в нашей доменной модели являются специальными, вы можете принять определенные меры для них, в зависимости от типа. Таким образом, логика привязана к типу. Для действия, которое требуется выполнить, требуется строка, привязанная к типу. Я мог бы создать экземпляр каждый раз в качестве обходного пути и объявить абстрактное свойство, но это не то, что я хочу. Я хочу принудительно объявить строку во время компиляции, чтобы быть уверенным.

4b9b3361

Ответ 1

Нет, вы не можете. Я бы предложил вам сделать абстрактный базовый класс с абстрактным свойством, которое вы можете получить, когда захотите. Каждый дочерний класс может затем реализовать свойство, просто вернув константу, если захочет. Недостатком является то, что вы не можете использовать это в статических методах в базовом классе, но в любом случае они не связаны с дочерними классами.

(Он также позволяет дочерним классам настраивать свойство для каждого экземпляра, если это необходимо... но это редко актуальная проблема.)

Если этого недостаточно для вас, вы можете рассмотреть иерархию параллельного типа. В основном полиморфизм просто не происходит в специфическом для типа образом в .NET; только по конкретному экземпляру.

Если вы все еще хотите сделать это и получить его с отражением, я предлагаю вам просто написать модульные тесты, чтобы определить соответствующие константы. Когда вы выходите за пределы того, что может описать система типов, это часто лучшее, что вы можете сделать.

Ответ 2

Сделайте abstract property только с get. То, что я думаю, что вы могли бы сделать для обеспечения соблюдения класса, имеет ценность. Затем вы можете просто вернуть константу в свойстве.

например:

Базовый класс:

public abstract string MyConst { get; }

Затем в производном классе

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

Ответ 3

Вот как я сделал свою работу. Я использовал атрибут, как предложили другие.

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;

Ответ 4

Новая идея

Вот какая-то странная идея: вместо непосредственного использования наследования вы создаете отдельный класс, чтобы обеспечить постоянное значение для каждого типа, полученного из некоторого типа 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>();

Оригинальный ответ

Возможно, вам это не понравится, но самый близкий способ выполнить это - объявить абстрактное свойство только для чтения (no set).

Если у вас есть экземпляр вашего подкласса, это может работать так же хорошо, как и константа, даже если это технически уровень экземпляра (он будет одинаковым для всех экземпляров данного класса).

Рассмотрим, например, IList.IsReadOnly. В большинстве случаев это на самом деле свойство, которое сообщает вам об основной реализации класса, в отличие от любого состояния, специфичного для конкретного экземпляра. (Это может быть член интерфейса, а не абстрактный член класса, но это та же идея.)

Если вы пытаетесь получить доступ к нему статически, хорошо... тогда вам не повезло. Но в этом случае я не вижу, как бы вы получили значение, не используя рефлексию. Возможно, это ваше намерение; Я не знаю.

Ответ 5

У вас может быть статический метод в базовом классе, называемый, например, "Register", который передает значение типа и константы с намерением быть вызванным конструкторами классов подтипов. Затем добавьте проверку во всех конструкторах базового класса, что построенный объект имеет зарегистрированный тип.

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 для своего реестра, если задействован поток)

Ответ 6

Есть еще один метод, который не был охвачен, и он использует модификатор 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
{
}