使用属性来简化属性

本文关键字:属性 | 更新日期: 2023-09-27 18:16:44

我有一个场景,我需要类中的属性映射到字典。下面是一个代码示例:

public string Foo
{
    get
    {
        if (!PropertyBag.ContainsKey("Foo"))
        {
            return null;
        }
        return PropertyBag["Foo"];
    }
    set
    {
        PropertyBag["Foo"] = value;
    }
}

我必须将此模式应用于多个属性。有没有办法使用属性来做到这一点?

我知道PostSharp会为这个目的工作,但我希望有一种方法可以做到这一点,而不使用它

使用属性来简化属性

对我来说这感觉像是代码的味道。最好使用常规poco,并仅在需要时将其转换为Dictionary。

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

启示:如何将class转换为Dictionary?

老实说,你的POCO上的ToDictionary方法看起来像一个代码气味。最好重构你的代码,让poco到字典的转换在它自己的层中进行,可能是作为一个服务。

编辑:这个要点是我在谷歌搜索"c#转换对象到字典"时发现的,可以提供一个更通用的解决方案,可能比我拼凑的例子更防弹:

要点: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.");
    }
}

来源:jerrettmeyer at GitHub

这应该为每个对象添加一个ToDictionary方法。

编辑:来自以下注释

给一点上下文,我正在使用实体框架,我有一个类层次结构,我想保持在一个表中,同时避免空列无处不在。

实体框架支持多表继承。

您可以编写一个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;
    }
}

您可以使用表达式消除魔术字符串。

如果你至少使用。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,另一种选择是利用另一个答案中建议的表达式树。

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