Как реализовать виртуальные статические свойства?

насколько я знаю C# не поддерживает виртуальные статические свойства. Как реализовать такое поведение в C#?

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

Type t = typeof(DerivedClass);
var identifier= (String) t.GetProperty("Identifier", BindingFlags.Static).GetValue(null, null);

3 ответов


для вас все еще нет ответа accpted около пяти лет спустя, позвольте мне попробовать(снова) ..

я никогда не думал о Любопытно Повторяющийся Шаблон Шаблон в качестве обходного пути, но так как вы откроете BaseClass для наследства это не было бы хорошей идеей. Возможно, вы захотите взглянуть на г-н Липперт это для лучшего понимания того, почему.

  • Решение 1: ты не Регистрация, я не узнаю ..

    public abstract class BaseClass {
        protected static void Register<U>(String identifier) where U : BaseClass {
            m_identities.Add(typeof(U).GetHashCode(), identifier);
        }
    
        public static String GetIdentifier<U>() where U : BaseClass {
            var t = typeof(U);
            var identifier = default(String);
            RuntimeHelpers.RunClassConstructor(t.TypeHandle);
            m_identities.TryGetValue(t.GetHashCode(), out identifier);
            return identifier;
        }
    
        static Dictionary<int, String> m_identities = new Dictionary<int, String> { };
    }
    
    public class DerivedClassA:BaseClass {
        static DerivedClassA() {
            BaseClass.Register<DerivedClassA>("12dc2490-065d-449e-a199-6ba051c93622");
        }
    }
    
    public class DerivedClassB:BaseClass {
        static DerivedClassB() {
            BaseClass.Register<DerivedClassB>("9745e24a-c38b-417d-a44d-0717e10e3b96");
        }
    }
    

    тест:

    Debug.Print("{0}", BaseClass.GetIdentifier<DerivedClassA>());
    Debug.Print("{0}", BaseClass.GetIdentifier<DerivedClassB>());
    

это относительно простой шаблон через инициализатор типа. The Register метод подвергается только производному классу; и оба GetIdentifier и Register методы ограничены для вызова с аргументом типа, который является производным от BaseClass. Хотя мы не заставляем производные классы переопределять что-либо, если оно не регистрируется, GetIdentifier не признает он и возвращается null.

  • решение 2: прежде чем вы покажете свою личность, я куплю вам по умолчанию. Кем бы вы себя ни считали, я верю-до тех пор, пока нет двусмысленности.

    public abstract class BaseClass {
        public abstract String Identifier {
            get;
        }
    
        public static Type GetDerivedClass(String identifier) {
            return m_aliases[identifier];
        }
    
        public static String GetIdentifier(Type t) {
            var value = default(String);
    
            if(t.IsSubclassOf(typeof(BaseClass))) {
                var key = t.GetHashCode();
    
                if(!m_identities.TryGetValue(key, out value)) {
                    value=""+key;
                    m_aliases.Add(value, t);
                    m_identities[key]=value;
                }
            }
    
            return value;
        }
    
        static void UpdateAlias(BaseClass x) {
            var t = x.GetType();
            var value = x.Identifier;
            m_aliases.Add(value, t);
            m_identities[t.GetHashCode()]=value;
        }
    
        protected BaseClass() {
            BaseClass.UpdateAlias(this);
        }
    
        static Dictionary<String, Type> m_aliases = new Dictionary<String, Type> { };
        static Dictionary<int, String> m_identities = new Dictionary<int, String> { };
    }
    

    public class DerivedClassA:BaseClass {
        public override String Identifier {
            get {
                return "just text";
            }
        }
    }
    
    public class DerivedClassB:BaseClass {
        public override String Identifier {
            get {
                return "just text";
            }
        }
    }
    

    и тест:

    public static void TestMethod() {
        var idBeforeInstantiation = BaseClass.GetIdentifier(typeof(DerivedClassA));
        var y = new DerivedClassA { };
        var idAfterInstantiation = BaseClass.GetIdentifier(typeof(DerivedClassA));
    
        Debug.Print("B's: {0}", BaseClass.GetIdentifier(typeof(DerivedClassB)));
        Debug.Print("A's after: {0}", idAfterInstantiation);
        Debug.Print("A's before: {0}", idBeforeInstantiation);
        Debug.Print("A's present: {0}", BaseClass.GetIdentifier(typeof(DerivedClassA)));
    
        var type1 = BaseClass.GetDerivedClass(idAfterInstantiation);
        var type2 = BaseClass.GetDerivedClass(idBeforeInstantiation);
    
        Debug.Print("{0}", type2==type1); // true
        Debug.Print("{0}", type2==typeof(DerivedClassA)); // true
        Debug.Print("{0}", type1==typeof(DerivedClassA)); // true
    
        var typeB=BaseClass.GetDerivedClass(BaseClass.GetIdentifier(typeof(DerivedClassB)));
    
        var x = new DerivedClassB { }; // confilct
    }
    

видимо, это более сложное решение. Как видите idBeforeInstantiation и idAfterInstantiation разные, однако, они являются либо допустимыми идентификаторами DerivedClassA. m_identities содержит последний обновленный идентификатор для каждого производного класса и m_aliases будет содержать все псевдонимы идентификаторов для производных классов. Так как сочетание виртуальный и static в настоящее время не является особенностью языка(возможно, никогда ..), если мы хотим принудительно переопределить тогда мы должны сделать это через некоторое обходное решение. Если вы выбираете solution2, вы, возможно, захотите, чтобы implemenet вы владеете UpdateAlias для предотвращения производной классы от предоставления слишком большого количества различных псевдонимов для одного типа, хотя все они будут действительны. Последнее утверждение в тесте ставится намеренно, чтобы продемонстрировать конфликт идентификаторов.

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


проще говоря, вы не можете, поэтому я смиренно предлагаю вам оставить его и попробовать что-то еще.

пожалуйста, смотрите ответ в это так пост. Если ты ... --5-- > мог бы реализовать такую функцию вы бы серьезные проблемы с наследованием.

был там, сделал это. После того, как я снова пришел в себя, я пошел на регулярный подход к наследованию. Думаю, тебе стоит сделать то же самое.


другой способ сделать это, который не требует регистрации класса, но требует небольшой дополнительной работы, - создать статический класс, который содержит "статические" данные для каждого типа производного класса и возвращает постоянное/статическое значение из статического класса. Позвольте мне объяснить специфику этого подхода.

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

Если вам нужно виртуальное поле или свойство, которое всегда одинаково для каждого члена класса (статическое), используйте нестатическое свойство, которое возвращает "константу" или статические данные, такие как это:

public static class MyStaticData
{
    public static const string Class1String = "MyString1";
    public static const int Class1Int = 1;
    public static const string Class2String = "MyString2";
    public static const int Class2Int = 2;
    // etc...
}

public abstract class MyBaseClass
{
    public abstract string MyPseudoVirtualStringProperty { get; }
    public abstract int MyPseudoVirtualIntProperty { get; }
}

public class MyDerivedClass1 : My BaseClass
{
    public override string MyPseudoVirtualStringProperty { get { return MyStaticData.Class1String; } }
    public override int MyPseudoVirtualIntProperty { get { return MyStaticData.Class1Int } }
}

public class MyDerivedClass2 : My BaseClass
{
    public override string MyPseudoVirtualStringProperty { get { return MyStaticData.Class2String; } }
    public override int MyPseudoVirtualIntProperty { get { return MyStaticData.Class2Int } }
}