如何实现在C#中具有属性作为属性的类?

时间:2015-02-03 11:48:14

标签: c#

我需要将程序选项/参数关联到ViewModel类属性(绑定到视图的控件)。所以我想创建一个类。

internal class OptionValue
{
    OptionSource Source { get; set; }
    Type OptionType { get; set; }
    string OptionName { get; set; }
    // ?? Property { get; set;} ??
    object DefaultValue { get; set; }
}

然后我会在OptionsViewModel类中创建一个IEnumerable并用它做一些事情。

public class OptionsViewModel : ViewModel
{
    OptionValue[] Options = 
    {
        new OptionValue(OptionSource.SysParameter, typeof(Int32), "SelectorSingleSelection", SingleSelection, 0),
        new OptionValue(OptionSource.ConfigFileProperty, typeof(Double), "SelectorTolerance", Tolerance, 0.002),
        ...
    }

    public int SingleSelection { get; set; }
    public double Tolerance { get; set; }


    void Init()
    {
        foreach (var optVal in Options)
        {
            FetchValueFromOptionSourceAndSetToProperty(optVal);
        }
    }

    void Save()
    {
        foreach (var optVal in Options)
        {
            StoreValueFromPropertyToOptionSource(optVal);
        }
    }

我完全失去了怎么做。 OptionSource是我的枚举,对此问题没有任何作用。

1 个答案:

答案 0 :(得分:1)

目前还不完全清楚你在这里问什么,因此投票结果非常接近。尽管如此,我认为评论者Jon Skeet猜测你似乎正在寻找一种通过名称动态访问属性的方法可能是正确的。所以考虑到这一点...

执行您似乎要求的事情的最明显,直接的方法实际上是使用PropertyInfo类型。例如......

abstract class OptionValue<TTarget>
{
    private PropertyInfo _propertyInfo;

    public TTarget TargetObject { get; private set; }
    public OptionSource Source { get; private set; }
    public string OptionName { get { return _propertyInfo.Name; } }

    protected OptionValue(TTarget targetObject, OptionSource source, string optionName)
    {
        TargetObject = targetObject;
        Source = source;

        _propertyInfo = typeof(TTarget).GetProperty(optionName);
    }

    public object GetValue()
    {
        return _propertyInfo.GetValue(TargetObject);
    }

    public void SetValue(object value)
    {
        _propertyInfo.SetValue(TargetObject, value);
    }

    public abstract void SetValueFromText(string text);

    public static OptionValue<TTarget, TProperty> Create<TTarget, TProperty>(
        TTarget targetObject, TProperty defaultValue, OptionSource source, string optionName)
    {
        return new OptionValue<TTarget, TProperty>(targetObject, defaultValue, source, optionName);
    }
}

class OptionValue<TTarget, TProperty> : OptionValue<TTarget>
{
    public TProperty DefaultValue { get; private set; }

    public OptionValue(TTarget targetObject, TProperty defaultValue,
        OptionSource source, string optionName)
        : base(targetObject, source, optionName)
    {
        DefaultValue = defaultValue;
    }

    public TProperty GetValue()
    {
        return (TProperty)base.GetValue();
    }

    public void SetValue(TProperty value)
    {
        base.SetValue(value);
    }

    public override void SetValueFromText(string text)
    {
        SetValue((TProperty)Convert.ChangeType(text, typeof(TProperty)));
    }
}

我重构了一点,为类型添加了通用语法,以防您的使用场景变得更容易。抽象基类几乎拥有您需要的一切;使用泛型确实使得SetValueFromText()更容易实现,但是可以通过使用属性中声明的类型将其移动到基类中。

您可以使用以上内容:

class OptionsViewModel
{
    OptionValue<OptionsViewModel>[] Options;

    public OptionsViewModel()
    {
        Options = new OptionValue<OptionsViewModel>[]
        {
            OptionValue<OptionsViewModel>.Create(this, 0, OptionSource.SysParameter, "SingleSelection"),
            OptionValue<OptionsViewModel>.Create(this, 0.002, OptionSource.ConfigFileProperty, "Tolerance"),
        };
    }

    public int SingleSelection { get; set; }
    public double Tolerance { get; set; }

    public void Init()
    {
        foreach (OptionValue<OptionsViewModel> option in Options)
        {
            FetchValueFromOptionSourceAndSetToProperty(option);
        }
    }

    // Signature and implementation modified a bit just for illustration purposes.
    public string Save()
    {
        StringBuilder sb = new StringBuilder();

        foreach (OptionValue<OptionsViewModel> option in Options)
        {
            sb.AppendLine(StoreValueFromPropertyToOptionSource(option));
        }

        return sb.ToString();
    }

    private void FetchValueFromOptionSourceAndSetToProperty(OptionValue<OptionsViewModel> option)
    {
        // Obviously, some actual code that would retrieve values would
        // go here for a real program.
        switch (option.OptionName + "Selector")
        {
            case "SingleSelectionSelector":
                option.SetValue(17);
                break;
            case "ToleranceSelector":
                option.SetValueFromText("3.141592");
                break;
        }
    }

    private string StoreValueFromPropertyToOptionSource(OptionValue<OptionsViewModel> option)
    {
        // Likewise, in your actual code you wouldn't even return
        // anything here, and the code would actually store the value
        // to some appropriate location.
        return option.GetValue().ToString();
    }
}

当然,在上面我已经修改了你的原始示例,以便代码可以在一个简单的测试程序中轻松运行,而不是要求你实际工作的任何更大的环境。 / p>

最后,一个简单的测试程序来说明上述工作:

class Program
{
    static void Main(string[] args)
    {
        OptionsViewModel model = new OptionsViewModel();

        model.Init();
        Console.WriteLine(model.Save());
    }
}

请注意,以这种方式使用PropertyInfo类会产生一些性能损失。假设这些操作不经常进行,和/或在I / O环境中进行,这可能不是问题。

但请注意,还有其他方法可以完成同样的事情。这些替代方案包括:

  1. 获取PropertyInfo,然后将其与Expression类一起使用,以编译委托实例以设置属性值。
  2. 只需让调用者通过将其明确地写为匿名方法来提供必要的委托实例。
  3. 完全改变整个架构并使用.NET内置的一种序列化方法。
  4. 我希望上面能让你走上正轨。您问题中的原始代码示例相当模糊,并且遗漏了您希望使用此Options类的 的非常重要的细节。我认为,鉴于上述推动正确的方向,你可以根据自己的需要调整概念。