如何实现虚拟静态属性
本文关键字:虚拟 静态 属性 实现 何实现 | 更新日期: 2023-09-27 18:03:27
据我所知C#
不支持虚拟静态属性。如何在C#
中实现这样的行为?
我想实现一个基类的所有派生类必须覆盖一个静态属性。获取一个派生类型,我想访问一个名为Identifier
Type t = typeof(DerivedClass);
var identifier= (String) t.GetProperty("Identifier", BindingFlags.Static).GetValue(null, null);
对于那些有同样想法并通过谷歌找到这篇文章的人,请考虑抽象工厂模式,而不是这里的解决方案。
,
5年过去了,你仍然没有一个公认的答案,让我再试一次。我曾经考虑过将奇怪的循环模板模式作为一种解决方案,但是由于您将打开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>());
通过类型初始化器,这是一个相对简单的模式。Register
方法只暴露给派生类;并且GetIdentifier
和Register
方法都被限制使用从BaseClass
派生的类型参数来调用。虽然我们没有强制派生类重写任何东西,但如果它没有注册自己,GetIdentifier
不会识别它并返回null
。
方案二:在你显示你的身份之前,我给你买一个默认值。不管你认为你是谁,我都相信——只要不模棱两可。
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
将包含派生类的所有标识符别名。由于虚拟和静态的组合目前不是语言的特性(可能永远不会…),如果我们想要强制覆盖,那么我们必须通过一些变通来实现。如果您选择解决方案2,您可能希望实现自己的UpdateAlias
,以防止派生类为单一类型提供太多不同的别名,尽管它们都是有效的。测试中的最后一条语句是故意放置的,以演示标识符的冲突。
因为这两个解决方案都是精心设计的,考虑到不实例化派生类,所以它们都不需要实例化。
c# 10在接口中引入了静态抽象方法并简化了解决方案。
使用这个新的语言特性,您可以将派生类标记为实现标识符属性的静态属性:
public interface IClass
{
static abstract string Identifier { get; }
}
public class DerivedClass1 : IClass
{
public static string Identifier => "DerivedClass1";
}
public class DerivedClass2 : IClass
{
public static string Identifier => "DerivedClass2";
}
为了使用它,你需要。net 6,并在你的应用程序的csproj文件中设置你的LangVersion预览。
参见microsoft文档:https://learn.microsoft.com/en-us/dotnet/core/compatibility/core-libraries/6.0/static-abstract-interface-methods
简单地说,你不能,所以我谦卑地建议你离开它并尝试其他东西。
请在这篇文章中看到答案。如果可以实现这样的特性,那么在继承方面就会出现严重的问题。
我也经历过。当我恢复理智后,我选择了常规的继承方法。我觉得你也应该这么做。
另一种不需要注册类但需要一点额外工作的方法是创建一个静态类,该类为每个派生类类型保存"静态"数据,并从静态类返回一个常量/静态值。让我来解释一下这种方法的细节。
对于类的每个成员来说,静态属性总是相同的一个重要原因是为了避免不必要的内存使用和重复。虽然这里演示的方法并不能完全避免这种情况,但它仍然绕过了大部分"额外"开销。下面的示例不能满足的唯一用例是,如果使用静态属性的原因是,您不必拥有实例,因为您必须拥有实例才能获得数据。如果需要对类的每个成员(静态)始终相同的虚拟字段或属性,请使用返回"常量"或静态数据的非静态属性:
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 } }
}