简体   繁体   English

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

[英]Overridable methods cannot be static: How else can I do what I'm trying to do?

I have a series of static classes that I use to get strings for enum values. 我有一系列静态类,用于获取枚举值的字符串。 They all look something like this: 他们都看起来像这样:

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

I have multiple classes like this, that differ in the Enum type they use, and the string values. 我有这样的多个类,它们使用的Enum类型和字符串值不同。 Clearly, I should combine the classes to reduce duplicated code. 显然,我应该结合使用这些类来减少重复的代码。

What I tried doing was create generic base class so that it can handle any type, then implement the PopulateMap for the inherited classes. 我尝试做的是创建通用基类,以便它可以处理任何类型,然后为继承的类实现PopulateMap。 If it were possible, it would look something like this: 如果有可能,它看起来像这样:

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

I had to make the Dictionary and the method PopulateMap public, because apparently generic classes cannot have protected or protected-internal members. 我不得不将字典和方法PopulateMap公开,因为显然泛型类不能有protected或protected-internal成员。 Having to make that public isn't ideal, but not a deal-breaker. 必须公开这个并不理想,但不是一个交易破坏者。

What I am getting hung up on is the fact that "overridable methods cannot be static", so my PopulateMap method cannot be both abstract and static. 我所依赖的是“可覆盖的方法不能是静态的”,所以我的PopulateMap方法既不能是抽象的,也不能是静态的。 And if it's not static, it can't be called from other static methods. 如果它不是静态的,则无法从其他静态方法调用它。 And if it's not abstract, then the inheriting classes' PopulateMap doesn't get called. 如果它不是抽象的,那么继承类的PopulateMap就不会被调用。

This version doesn't even build. 这个版本甚至没有构建。

Is there any way to do what I'm trying to do and still keep my class static? 有没有办法做我想做的事情,仍然保持我的班级静态? I'd really like to avoid having to have an instantiated TypeToString object every time I want to call TypeToString.Get(). 每次我想调用TypeToString.Get()时,我真的想避免必须有一个实例化的TypeToString对象。

Here's a handy extension method, as I'm guessing you're trying to map some description text to an enum value: 这是一个方便的扩展方法,因为我猜你正在尝试将一些描述文本映射到枚举值:

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

Use it like this: 像这样使用它:

public enum Foo
{
    [Description("Hello")]
    Bar,

    [Description("World")]
    Baz
}

var value = Foo.Bar;
var description = value.GetDescription(); // Hello

Depending on your needs, you could cache the descriptions if reflection proves to be too slow for you, just modify the GetDescription method. 根据您的需要,如果反射被证明对您来说太慢,您可以缓存描述,只需修改GetDescription方法即可。


EDIT: to account for the additional info in the comment. 编辑:考虑评论中的其他信息。

As it looks like you need something more extensible, you could use a custom attribute: 由于看起来您需要更具可扩展性的东西,您可以使用自定义属性:

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

Which would let you to do this: 哪个可以让你这样做:

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

    [DescriptionEntry("Name", "World")]
    [DescriptionEntry("Title", "Some title")]
    Baz
}

Now, to read this thing, I'd advise you to store it in a cache like that: 现在,要阅读此内容,我建议您将其存储在这样的缓存中:

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

This way: 这条路:

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

That should be enough to get you started, now you should tweak it to your needs. 这应该足以让你开始,现在你应该调整它以满足你的需求。 For instance, you could use an enum instead of a string for the keys, it would help avoid typing mistakes, but I don't know if this would suit your needs. 例如,您可以使用枚举而不是字符串作为键,这有助于避免键入错误,但我不知道这是否适合您的需要。

Your problem is that static methods and variables are essentially not inherited. 您的问题是静态方法和变量基本上不会被继承。 They are variables that don't act on instances of the class themselves, but provide some functionality to the class. 它们是不对类本身实例起作用的变量,但为类提供了一些功能。

So you have a bunch of different enums, and you want to populate them based on different stuff. 所以你有一堆不同的枚举,你想根据不同的东西填充它们。 So let's look at what parts you have, and what is common: 那么让我们来看看你有哪些部件,以及什么是常见的:

  • PopulateMap: Not common PopulateMap:不常见
  • Enum type: Not common 枚举类型:不常见
  • Storage variable: Common 存储变量:常见
  • Populate Map if empty: Common 如果为空则填充地图:常见

So all you really want is a way to populate the map once, when it's used. 所以你真正想要的是一种在使用时填充地图的方法。 There is already a class for this, it's called Lazy . 已经有一个类,它叫做Lazy Using that, the code becomes: 使用它,代码变为:

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

Alternatively, you can decorate your enums with a [DescriptionAttribute] and then create a method to get the description of a specific enum. 或者,您可以使用[DescriptionAttribute]修饰您的枚举,然后创建一个方法来获取特定枚举的描述。 This is what I did when I was faced with a similar problem. 这就是我遇到类似问题时所做的。 (Be sure to cache the results for the enum, as it used reflection which was slow.) (确保缓存枚举的结果,因为它使用了很慢的反射。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何解决虚拟实体框架对象的“不要在构造函数中调用可覆盖的方法”警告? - How can I resolve a “Do not call overridable methods in constructors” warning for virtual Entity Framework objects? 当我检查网站是否在webBrowser DocumentCompleted中完成上传时,所有内容都处于“交互”状态,我还能做什么? - When i'm checking if site uploaded complete in webBrowser DocumentCompleted it's all the in Interactive state what else can i do? 为什么不在构造函数中调用可覆盖的方法? - Why do not call overridable methods in constructors? MSOCAF验证-不要在构造函数中调用可重写的方法 - MSOCAF Verification - Do not call overridable methods in constructors 我无法继承通过DllImport导入的方法。 该怎么办? - I cannot inherit methods imported via DllImport. What to do? 如何在基类中定义可以在泛型方法中调用的静态变量? - How do I define a static variable in base class that can be called in generic methods? C#如何将AOP用于静态方法? - C# How do I use AOP for static methods? 如何使用扩展方法或Linq做到这一点? - How can i do this with Extensions methods or Linq? 如何缩短If else - How do I to shorten If else 我可以做些什么来重新加载/重新实例化 static class? - What can I do for a static class to be reloaded/reinstantiated?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM