如何实现虚拟静态属性?

Raz*_*zer 9 c# virtual static properties

据我所知C#,不支持虚拟静态属性.如何实现这样的行为C#

我想归档基类的所有派生类必须覆盖静态属性.获取派生类型,我想访问一个名为的静态属性Identifier

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

Arm*_*and 7

C# 10 在接口中引入了静态抽象方法并简化了解决方案。

\n

使用这个新的语言功能,您可以标记派生类以将标识符属性实现为静态属性:

\n
public interface IClass\n{\n    static abstract string Identifier { get; }\n}\n\npublic class DerivedClass1 : IClass\n{\n    public static string Identifier => "DerivedClass1";\n}\n\npublic class DerivedClass2 : IClass\n{\n    public static string Identifier => "DerivedClass2";\n}\n
Run Code Online (Sandbox Code Playgroud)\n

为了使用它,您需要 .NET 6 并将 LangVersion 设置为在 application\xe2\x80\x99s csproj 文件中预览。

\n

请参阅微软的文档:\n https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/static-abstract-interface-methods

\n


Ken*_*Kin 6

对于思考同一件事并通过谷歌搜索到达这篇文章的人,请考虑抽象工厂模式而不是这里的解决方案。

——

因为大约五年后你仍然没有一个公认的答案,让我试一试(再次)..

我曾经想过将Curiously Recurring Template Pattern作为一种解决方法,但由于您将开放BaseClass继承,这不是一个好主意。您可能需要查看Lippert 先生的博文,以便更好地了解原因。

  • 解决方案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");
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    测试:

    Debug.Print("{0}", BaseClass.GetIdentifier<DerivedClassA>());
    Debug.Print("{0}", BaseClass.GetIdentifier<DerivedClassB>());
    
    Run Code Online (Sandbox Code Playgroud)

这是通过类型初始化器的一个相对简单的模式。该Register方法只暴露给派生类;并且GetIdentifierRegister方法都被限制为使用派生自 的类型参数调用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> { };
    }
    
    Run Code Online (Sandbox Code Playgroud)
    public class DerivedClassA:BaseClass {
        public override String Identifier {
            get {
                return "just text";
            }
        }
    }
    
    public class DerivedClassB:BaseClass {
        public override String Identifier {
            get {
                return "just text";
            }
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    和测试:

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

显然,这是一个更复杂的解决方案。正如你所看到的idBeforeInstantiationidAfterInstantiation是不同的,但是,它们要么是有效标识符DerivedClassAm_identities包含每个派生类的最后更新标识符,m_aliases并将包含派生类的所有标识符别名。由于虚拟静态的组合当前不是该语言的功能(可能永远不会......),如果我们想要强制覆盖,那么我们必须通过一些解决方法来完成。如果您选择解决方案 2,您可能想要实现自己的UpdateAlias防止派生类为单个类型提供过多的各种别名,尽管它们都是有效的。测试中的最后一条语句是故意放置以证明标识符的冲突。

因为这两个解决方案是为您考虑不实例化派生类而精心设计的,它们都不需要