Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
268 views
in Technique[技术] by (71.8m points)

c# - How to implement virtual static properties?

As far as I know C# doesn't support virtual static properties. How to implement such a behavior in C#?

I want to archive that all derived classes of a base class must override a static property. Getting a derived type, I want to access to a static property called Identifier

Type t = typeof(DerivedClass);
var identifier= (String) t.GetProperty("Identifier", BindingFlags.Static).GetValue(null, null);
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

For people who think about the same thing and reach this post by googling, consider abstract factory pattern rather than the solutions here.

--

For you still don't have an accpted answer about five years later, let me give it a try(again) ..

I've ever thought about the Curiously Recurring Template Pattern as a workaround, but since you'll open BaseClass for inheritance it would not be a good idea. You might want to have a look at Mr. Lippert's blogpost for a better understanding of why.

  • Solution 1: You don't register, I don't recognize ..

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

    test:

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

This is is a relatively simple pattern through the type initializer. The Register method is only exposed to derived class; and both the GetIdentifier and Register methods are constrained to be invoked with a type argument which is derived from BaseClass. Although we don't force the derived classes to override anything, if it doesn't register itself, GetIdentifier doesn't recognize it and returns null.

  • Solution 2: Before you show your identity, I buy you a default. Whoever you think you are, I believe -- as long as there are no ambiguity.

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

    and the test:

    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
    }
    

Apparently this is a more complicated solution. As you can see idBeforeInstantiation and idAfterInstantiation are different, however, they are either valid identifiers for DerivedClassA. m_identities contains the last updated identifier for each derived class and m_aliases will contain all the identifier aliases for the derived classes. Since the combination of virtual and static is not a feature of the language currently(maybe never ..), if we want enforce the override then we have to do it through some workaround. If you'll choose solution2, You might want to implemenet you own UpdateAlias to prevent the derived classes from providing too much of various aliases for a single type, though they will all be valid. The last statement in the test are put deliberately to demonstrate the conflict of identifiers.

For these two solutions are carefully designed for your consideration of not to instantiate the derived classes, none of them requires that.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...