2013-03-11 5 views
7

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

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

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

Статические члены не могут быть переопределены (или это «переопределено»?) Pardon my bad english = ( –

+0

Как вы сказали, C# не поддерживает его, поэтому вы не можете его реализовать. – Servy

+0

В любом случае, для чего вы хотите это сделать? такое же может быть достигнуто с помощью 'Attributes'. –

ответ

1

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

я когда-либо думали о Curiously Recurring Template Pattern как обходной путь, но так как вы» ll открыть BaseClass для наследования это не будет хорошей идеей. Возможно, вы захотите взглянуть на Mr. Lippert's blogpost, чтобы лучше понять, почему.

  • Решение 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>()); 
    

Это является относительно простой шаблон через инициализацию типа р. Метод 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 будет содержать все псевдонимы идентификатора для производных классов. Так как комбинация virtual и static не является признаком данного языка (возможно, никогда ..), если мы хотим обеспечить отмену переопределения, тогда мы должны сделать это через некоторое обходное решение. Если вы выберете решение2, вы можете захотеть реализовать свой собственный UpdateAlias, чтобы не допустить, чтобы производные классы предоставляли слишком много различных псевдонимов для одного типа, хотя все они будут действительны. Последнее утверждение в тесте преднамеренно демонстрирует конфликт идентификаторов.

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

4

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

См. Ответ в сообщении this SO. Если вы можете реализовать такую ​​функцию, вы бы получили серьезные проблемы с наследованием.

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

0

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

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

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

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 } } 
} 
Смежные вопросы