可重写的方法不能是静态的:否则我怎么能做我想做的事情呢?

本文关键字:怎么能 不能 方法 静态 可重写 | 更新日期: 2023-09-27 18:19:18

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

public static class MyEnumToString
{
  private static Dictionary<MyEnum, string> map
   = new Dictionary<MyEnum, string>();
  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";
  }
}

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

我尝试做的是创建泛型基类,以便它可以处理任何类型,然后为继承的类实现PopulateMap。如果可能的话,它看起来应该是这样的:

public static class TypeToString<TType>
{
  public static Dictionary<TType, string> map
   = new Dictionary<TType, string>();
  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<MyEnum>
{
  public static void PopulateMap()
  {
    map[MyEnum.enum1] = "string for enum 1";
    map[MyEnum.enum2] = "string for enum 2";
  }
}

我必须将Dictionary和PopulateMap方法设为公共,因为显然泛型类不能有受保护的成员或受保护的内部成员。不得不公开这一点是不理想的,但不是一个交易破坏者。

我所得到的是"可重写的方法不能是静态的"这一事实,所以我的PopulateMap方法不能既抽象又静态。如果它不是静态的,它就不能从其他静态方法调用。如果它不是抽象的,那么继承类的PopulateMap就不会被调用。

这个版本甚至没有构建。

是否有任何方法可以做我想做的事情,并且仍然保持我的类静态?我真的希望避免每次调用TypeToString. get()时都必须有一个实例化的TypeToString对象

可重写的方法不能是静态的:否则我怎么能做我想做的事情呢?

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

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<DescriptionAttribute>()
                             .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

根据您的需要,如果反射证明对您来说太慢,您可以缓存描述,只需修改GetDescription方法。


EDIT:说明评论中的附加信息。

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

[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<Type, DescriptionCache> Caches = new ConcurrentDictionary<Type, DescriptionCache>();
    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<TEnum> GetValuesFromDescription<TEnum>(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<Enum, Tuple<string, string>> _items;
        private readonly ILookup<Tuple<string, string>, Enum> _reverse;
        public DescriptionCache(Type enumType)
        {
            if (!enumType.IsEnum)
                throw new ArgumentException("Not an enum");
            _items = (from value in Enum.GetValues(enumType).Cast<Enum>()
                      let field = enumType.GetField(value.ToString())
                      where field != null
                      from attribute in field.GetCustomAttributes(typeof (DescriptionEntryAttribute), false).OfType<DescriptionEntryAttribute>()
                      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<Enum> GetValues(string key, string description)
        {
            return _reverse[Tuple.Create(key, description)];
        }
    }
}

:

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

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

你的问题是静态方法和变量本质上是不可继承的。它们是变量,不作用于类本身的实例,但为类提供一些功能。

你有一堆不同的枚举,你想用不同的东西填充它们。那么让我们来看看你有哪些部分,哪些是共同的:

  • PopulateMap: Not common
  • 枚举类型:不常见
  • 存储变量:Common
  • 填充地图如果为空:普通

所以你真正想要的是在地图被使用时一次填充它。已经有这样一个类了,叫做Lazy。使用它,代码变成:

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

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