我有一个场景,我需要在我的类中的属性映射到字典。这是一个代码示例:
public string Foo
{
get
{
if (!PropertyBag.ContainsKey("Foo"))
{
return null;
}
return PropertyBag["Foo"];
}
set
{
PropertyBag["Foo"] = value;
}
}
我必须将此模式应用于多个属性。有没有办法使用属性来做到这一点?
我知道PostSharp可以用于此目的,但我希望有一种方法可以不使用它。
答案 0 :(得分:1)
这对我来说就像是一种代码味道。最好使用常规POCO并仅在需要时将它们转换为字典。
public class BlogPost
{
public string Title { get; set; }
public string Body { get; set; }
public int AuthorId { get; set; }
public Dictionary<string, object> ToDictionary()
{
return this.GetType()
.GetProperties(BindingFlags.Instance | BindingFlags.Public)
.ToDictionary(prop => prop.Name, prop => prop.GetValue(this, null));
}
}
灵感:How to convert class into Dictionary?
说实话,POCO上的ToDictionary
方法看起来像代码味道。最好重构你的代码,以便将POCO转换为字典发生在它自己的层中,作为一种服务。
修改:我在搜索google时发现“g#convert object to dictionary”这个Gist可以提供更通用的解决方案,并且可能比我拼凑的例子更加防弹:
要点:https://gist.github.com/jarrettmeyer/798667
来自要点:
public static class ObjectToDictionaryHelper
{
public static IDictionary<string, object> ToDictionary(this object source)
{
return source.ToDictionary<object>();
}
public static IDictionary<string, T> ToDictionary<T>(this object source)
{
if (source == null)
ThrowExceptionWhenSourceArgumentIsNull();
var dictionary = new Dictionary<string, T>();
foreach (PropertyDescriptor property in TypeDescriptor.GetProperties(source))
AddPropertyToDictionary<T>(property, source, dictionary);
return dictionary;
}
private static void AddPropertyToDictionary<T>(PropertyDescriptor property, object source, Dictionary<string, T> dictionary)
{
object value = property.GetValue(source);
if (IsOfType<T>(value))
dictionary.add(property.Name, (T)value);
}
private static bool IsOfType<T>(object value)
{
return value is T;
}
private static void ThrowExceptionWhenSourceArgumentIsNull()
{
throw new ArgumentNullException("source", "Unable to convert object to a dictionary. The source object is null.");
}
}
来自GitHub的jerrettmeyer
这应该为每个对象添加ToDictionary
方法。
编辑:来自以下评论
为了给出一些上下文,我使用的是实体框架,我有一个类层次结构,我想保留在一个表中,同时避免在任何地方使用空列。
实体框架支持多表继承。在您的情况下,这可能是更好的解决方案。
答案 1 :(得分:0)
您可以编写GetValueOrDefault
扩展方法并稍微减少代码。
public static class DictionaryExtensions
{
public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey,TValue> self, TKey key)
{
TValue value;
self.TryGetValue(key,out value);
return value;
}
}
public string Foo
{
get
{
return PropertyBag.GetValueOrDefault("Foo");
}
set
{
PropertyBag["Foo"] = value;
}
}
答案 2 :(得分:0)
如果您使用的是至少.NET 4.5,那么您可以使用CallerMemberNameAttribute
,如下所示:
class SomeClass
{
public string Foo
{
get
{
return GetPropertyValue();
}
set
{
SetPropertyValue( value );
}
}
private string GetPropertyValue( [CallerMemberName] string name = null )
{
string value;
PropertyBag.TryGetValue( name, out value );
return value;
}
private void SetPropertyValue( string value, [CallerMemberName] string name = null )
{
PropertyBag[name] = value;
}
}
这将导致编译器为您填写成员的名称。如果你不是(或不能)使用.NET 4.5,另一种选择是利用另一个答案中建议的expression trees。
答案 3 :(得分:-1)
class Test
{
Dictionary<string,object> _values = new Dictionary<string, object>();
public string Foo
{
get
{
var value = GetValue();
return value == null ? string.Empty : (string)value;
}
set
{
SetValue(value);
}
}
private object GetValue()
{
var stack = new StackTrace();
var key = GetGenericName(stack.GetFrame(1).GetMethod().Name);
if (_values.ContainsKey(key)) return _values[key];
return null;
}
private void SetValue(object value)
{
var stack = new StackTrace();
var key = GetGenericName(stack.GetFrame(1).GetMethod().Name);
_values[key] = value;
}
private string GetGenericName(string key)
{
return key.Split('_')[1];
}
}