![](/img/trans.png)
[英]How can I resolve a “Do not call overridable methods in constructors” warning for virtual Entity Framework objects?
[英]Overridable methods cannot be static: How else can I do what I'm trying to do?
我有一系列靜態類,用於獲取枚舉值的字符串。 他們都看起來像這樣:
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";
}
}
我有這樣的多個類,它們使用的Enum類型和字符串值不同。 顯然,我應該結合使用這些類來減少重復的代碼。
我嘗試做的是創建通用基類,以便它可以處理任何類型,然后為繼承的類實現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";
}
}
我不得不將字典和方法PopulateMap公開,因為顯然泛型類不能有protected或protected-internal成員。 必須公開這個並不理想,但不是一個交易破壞者。
我所依賴的是“可覆蓋的方法不能是靜態的”,所以我的PopulateMap方法既不能是抽象的,也不能是靜態的。 如果它不是靜態的,則無法從其他靜態方法調用它。 如果它不是抽象的,那么繼承類的PopulateMap就不會被調用。
這個版本甚至沒有構建。
有沒有辦法做我想做的事情,仍然保持我的班級靜態? 每次我想調用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<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
方法即可。
編輯:考慮評論中的其他信息。
由於看起來您需要更具可擴展性的東西,您可以使用自定義屬性:
[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.Bar
和Foo.Baz
的序列 這應該足以讓你開始,現在你應該調整它以滿足你的需求。 例如,您可以使用枚舉而不是字符串作為鍵,這有助於避免鍵入錯誤,但我不知道這是否適合您的需要。
您的問題是靜態方法和變量基本上不會被繼承。 它們是不對類本身實例起作用的變量,但為類提供了一些功能。
所以你有一堆不同的枚舉,你想根據不同的東西填充它們。 那么讓我們來看看你有哪些部件,以及什么是常見的:
所以你真正想要的是一種在使用時填充地圖的方法。 已經有一個類,它叫做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]
修飾您的枚舉,然后創建一個方法來獲取特定枚舉的描述。 這就是我遇到類似問題時所做的。 (確保緩存枚舉的結果,因為它使用了很慢的反射。)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.