可覆盖的方法不能是静态的:我怎样才能做我想做的事情?

我有一系列静态类,用于获取枚举值的字符串。 他们都看起来像这样:

public static class MyEnumToString { private static Dictionary map = new Dictionary(); public static string Get(MyEnum type) { PopulateEmptyMap(); return map[type]; } static void PopulateEmptyMap() { if (!map.Any()) { PopulateMap(); } } private static void PopulateMap() { map[MyEnum.enum1] = "string for enum 1"; map[MyEnum.enum2] = "string for enum 2"; } } 

我有这样的多个类,它们使用的Enum类型和字符串值不同。 显然,我应该结合这些类来减少重复的代码。

我尝试做的是创建通用基类,以便它可以处理任何类型,然后为inheritance的类实现PopulateMap。 如果可能,它看起来像这样:

 public static class TypeToString { public static Dictionary map = new Dictionary(); public static string Get(TType type) { PopulateEmptyMap(); return map[type]; } static void PopulateEmptyMap() { if (!map.Any()) { PopulateMap(); } } public abstract static void PopulateMap(); } public static class MyEnumToString: TypeToString { public static void PopulateMap() { map[MyEnum.enum1] = "string for enum 1"; map[MyEnum.enum2] = "string for enum 2"; } } 

我不得不将字典和方法PopulateMap公之于众,因为显然generics类不能有受保护或受保护的内部成员。 必须公开这个并不理想,但不是一个交易破坏者。

我所依赖的是“可覆盖的方法不能是静态的”,因此我的PopulateMap方法既不能是抽象的,也不能是静态的。 如果它不是静态的,则无法从其他静态方法调用它。 如果它不是抽象的,那么inheritance类的PopulateMap就不会被调用。

这个版本甚至没有构建。

有没有办法做我想做的事情,仍然保持我的class级静态? 我真的想避免每次要调用TypeToString.Get()时都必须有一个实例化的TypeToString对象。

这是一个方便的扩展方法,因为我猜你正在尝试将一些描述文本映射到枚举值:

 public static class EnumExtensions { public static string GetDescription(this Enum value) { var field = value.GetType().GetField(value.ToString()); if (field == null) return value.ToString(); var attribute = field.GetCustomAttributes(typeof(DescriptionAttribute), false) .OfType() .SingleOrDefault(); return attribute != null ? attribute.Description : value.ToString(); } } 

像这样用它:

 public enum Foo { [Description("Hello")] Bar, [Description("World")] Baz } var value = Foo.Bar; var description = value.GetDescription(); // Hello 

根据您的需要,如果reflection被certificate对您来说太慢,您可以缓存描述,只需修改GetDescription方法即可。


编辑:考虑评论中的其他信息。

由于看起来您需要更具可扩展性的东西,您可以使用自定义属性:

 [AttributeUsage(AttributeTargets.Field, AllowMultiple = true, Inherited = false)] public sealed class DescriptionEntryAttribute : Attribute { public string Key { get; private set; } public string Value { get; private set; } public DescriptionEntryAttribute(string key, string value) { Key = key; Value = value; } } 

哪个可以让你这样做:

 public enum Foo { [DescriptionEntry("Name", "Hello")] [DescriptionEntry("Title", "Some title")] Bar, [DescriptionEntry("Name", "World")] [DescriptionEntry("Title", "Some title")] Baz } 

现在,为了阅读这个东西,我建议你将它存储在这样的缓存中:

 public static class EnumExtensions { private static readonly ConcurrentDictionary Caches = new ConcurrentDictionary(); public static string GetDescription(this Enum value, string key) { var enumType = value.GetType(); var cache = Caches.GetOrAdd(enumType, type => new DescriptionCache(type)); return cache.GetDescription(value, key); } public static IEnumerable GetValuesFromDescription(string key, string description) where TEnum : struct { var cache = Caches.GetOrAdd(typeof(TEnum), type => new DescriptionCache(type)); return cache.GetValues(key, description).Select(value => (TEnum)(object)value); } private class DescriptionCache { private readonly ILookup> _items; private readonly ILookup, Enum> _reverse; public DescriptionCache(Type enumType) { if (!enumType.IsEnum) throw new ArgumentException("Not an enum"); _items = (from value in Enum.GetValues(enumType).Cast() let field = enumType.GetField(value.ToString()) where field != null from attribute in field.GetCustomAttributes(typeof (DescriptionEntryAttribute), false).OfType() select new {value, key = attribute.Key, description = attribute.Value}) .ToLookup(i => i.value, i => Tuple.Create(i.key, i.description)); _reverse = (from grp in _items from description in grp select new {value = grp.Key, description}) .ToLookup(i => i.description, i => i.value); } public string GetDescription(Enum value, string key) { var tuple = _items[value].FirstOrDefault(i => i.Item1 == key); return tuple != null ? tuple.Item2 : null; } public IEnumerable GetValues(string key, string description) { return _reverse[Tuple.Create(key, description)]; } } } 

这条路:

  • Foo.Bar.GetDescription("Name")返回"Hello"
  • EnumExtensions.GetValuesFromDescription("Title", "Some title")返回包含Foo.BarFoo.Baz的序列

这应该足以让你开始,现在你应该调整它以满足你的需求。 例如,您可以使用枚举而不是字符串作为键,这有助于避免键入错误,但我不知道这是否适合您的需要。

您的问题是静态方法和变量基本上不会被inheritance。 它们是不对类本身实例起作用的变量,但为类提供了一些function。

所以你有一堆不同的枚举,你想根据不同的东西填充它们。 那么让我们来看看你有哪些部件,以及什么是常见的:

  • PopulateMap:不常见
  • 枚举类型:不常见
  • 存储变量:常见
  • 如果为空则填充地图:常见

所以你真正想要的是一种在使用时填充地图的方法。 已经有一个类,它叫做Lazy 。 使用它,代码变为:

 public abstract class TypeToString { protected TypeToString() { storage = new Lazy>(GetMap); } private Lazy> storage; protected abstract Dictionary GetMap(); public string Get(Type t) {return storage.Value[t];} } public class MyEnumToString : TypeToString { protected override Dictionary GetMap() { return null; } public static Get(MyEnum e) { return new MyEnumToString.Get(e); } } 

或者,您可以使用[DescriptionAttribute]修饰您的枚举,然后创建一个方法来获取特定枚举的描述。 这就是我遇到类似问题时所做的。 (确保缓存枚举的结果,因为它使用了很慢的reflection。)