有没有办法向 C# 中的对象添加 key/value 语法?

Is there a way to add key/value syntax to an object in C#?

我有一个名为 CottonCandy 的对象。 CottonCandy 有几个属性:

如果我可以向 CottonCandy 添加一些东西以便我可以像这样检索属性,那就太好了:

var colors = cottonCandy["Colors"];

从阅读 this 看来,您可以通过以下方式获取值:

cottonCandy.GetType().GetProperty("Colors").GetValue(cottonCandy, null);

您可以轻松地将其包装在一个方法中:

var colors = cottonCandy.GetPropertyValue("Colors");

但我真的更喜欢 key/value 语法 cottonCandy["Colors"]。有什么办法可以实现吗?

如果您的 class 是一个简单的数据对象,没有任何行为,只有这么简单的属性,您可以将其设为 Dictionary

var map = new Dictionary<string, whatever> { { "Colors", ... }, { "Mass", ... } ... };
map["Colors"] = ...;

否则你应该像你已经提到的那样使用反射。

这不是 key/vaue 语法。但是,如果您决定使用扩展,则可以使用此扩展方法。此 Nuget

中有类似的扩展
public static T GetFieldValue<T>(this object obj, string fieldName)
{
    T result;
    PropertyInfo propertyInfo = obj.GetType().GetProperty(fieldName);
    if (propertyInfo != null)
    {
        result = (T)propertyInfo.GetValue(obj);
    }
    else
    {
        throw new FieldAccessException($"{fieldName} cannot be found");
    }
    return result;
}

您可以通过 this link. Maybe unit tests 提供有关方法使用和结果的线索来获得更多扩展。

更新:

您可以像这样组合扩展方法和索引器:

public class TestClass
{
    public string this[string field]
    {
        get => this.GetFieldValue<string>(field);
        set => this.TrySetFieldValue(field, value);
    }
}

其实是有办法的。您可以使用 indexers。但是您需要具有相同的字段类型或将值装在 object 类型中。

class CottonCandy
{
    private int Mass { get; set; }
    private int Volume { get; set; }
    private int TotalSugar { get; set; }
    private int Colors { get; set; }

    public int this[string field]
    {
        get
        {
            PropertyInfo propertyInfo = typeof(CottonCandy).GetProperty(field);
            if(propertyInfo == null)
                throw new ArgumentException("Invalid field");
            return (int)propertyInfo.GetValue(this);
        }
    }
}

尝试查看对象索引器:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/

实现此目的的一种方法是使用索引器并使用 "nameof" 运算符将索引器字符串值与可用属性进行比较。这会使代码更加冗长,但避免了反射的需要。

但是,请确保这确实是您真正想要的,因为如果您以这种方式访问​​属性,您将不再获得编译时检查的好处。

你可以做到这一点

public class CottonCandy
    {

        public string Mass { get; set; }
        public string Volume { get; set; }
        public string TotalSugar { get; set; }
        public string Colors { get; set; }
        public string this[string key]
        {
            get
            {
                switch (key)
                {
                    case "Mass":
                        return this.Mass;
                    case "Volume":
                        return this.Volume;
                    case "TotalSugar":
                        return this.TotalSugar;
                    case "Colors":
                        return this.Colors;
                    default:
                        return "";
                }

            }
            set
            {
                switch (key)
                {
                    case "Mass":
                        this.Mass = value; break;
                    case "Volume":
                        this.Volume = value; break;
                    case "TotalSugar":
                        this.TotalSugar = value; break;
                    case "Colors":
                        this.Colors = value; break;
                    default:
                        break;
                }
            }
        }


    }