如何正确声明约束

时间:2019-04-09 12:48:35

标签: c#

我有一个RuleCondition<TValue>班:

public class RuleCondition<TValue>
{
    public string PropertyToCheck { get; private set; }

    public TValue Value { get; private set; }

    public RuleCondition(string propertyToCheck, TValue value)
    {
        PropertyToCheck = propertyToCheck;
        Value = value;
    }
}

Rule<T>类中,我有一个包含它的实例的字段:

public abstract class Rule<T>
{
    private readonly string mPropertyName;
    private readonly object mError;
    private readonly RuleCondition<TValue> mCondition;

    protected Rule(string propertyName, object error, RuleCondition<TValue> condition)
    {
        mCondition = condition;
    }

    // ...
}

现在我想将我的RuleCondition<T>添加到Rule(继承自Collection<Rule<T>>的{​​{1}}和DelegateRule<T>的继承人)中

Rule<T>

我收到一个错误,指出类型不匹配,这是正确的。我现在的问题是:如何获得Rules.Add(new DelegateRule<ConnectionSettingsViewModel>(nameof(Username), "Username: Cannot be empty, starting with a space or a backslash.", x => !string.IsNullOrWhiteSpace(x.Username), new RuleCondition<LoginOptions>(nameof(LoginUsage), LoginOptions.Instrument))); 的{​​{1}},而不必在“ top”中声明它并破坏我的所有代码。只是为了在TValue类中获得约束而在各处声明约束是不对的。

有没有更简单的方法?我想念什么?

编辑RuleCondition已经是RuleCondition<TValue>类型。

我认为我必须添加更多解释:

“链”从此处开始:

T

如果现在像*ViewModel那样将public class ConnectionSettingsViewModel : NotifyDataErrorInfo<ConnectionSettingsViewModel> { } 约束添加到TValue类,则还需要将该约束提供给Rule类。因此,进入“顶级”类别。但这感觉不对。在Rule<T, TValue>和“最后一个”类NotifyDataErrorInfo之间的每个类中声明该约束。

编辑2: 我从here中获取了代码,以对我的MVVM应用程序实施控件验证。但是我认为我需要满足一些控制条件。因此,规则不会每次都“执行”,而只是在特定的NotifyDataErrorInfo例如被选中。

1 个答案:

答案 0 :(得分:1)

根据您的输入和该博客文章,您可以执行类似的操作。考虑到您需要稍微修改该博客中提供的类。

首先,您可以创建ConditionalRule<T, TValue>来继承DelegateRule<T>(或您想要的任何其他Rule<T>)。 为此,您需要取消密封DelegateRule

public class ConditionalRule<T, TValue> : DelegateRule<T>
{
    public TValue Value { get; private set; }

    public ConditionalRule(string propertyName, object error, Func<T, bool> rule, TValue value)
        : base(propertyName, error, rule)
    {
        Value = value;
    }

    public override bool Apply(T obj)
    {
        // do your business driven condition check before calling
        return base.Apply(obj);
    }
}

第二,您必须使用其他通用方法扩展RuleCollection<T>

public sealed class RuleCollection<T> : Collection<Rule<T>>
{
    #region Public Methods

    /// <summary>
    /// Adds a new <see cref="Rule{T}"/> to this instance.
    /// </summary>
    /// <param name="propertyName">The name of the property the rules applies to.</param>
    /// <param name="error">The error if the object does not satisfy the rule.</param>
    /// <param name="rule">The rule to execute.</param>
    public void Add(string propertyName, object error, Func<T, bool> rule)
    {
        this.Add(new DelegateRule<T>(propertyName, error, rule));
    }

    public void AddConditional<TValue>(string propertyName, object error, Func<T, bool> rule, TValue value)
    {
        this.Add(new ConditionalRule<T, TValue>(propertyName, error, rule, value)
    }

    //....
    #endregion
}

此外,您需要修改代码,以便将规则添加到集合中,具体取决于要添加的规则:

Rules.AddConditional<LoginOptions>(
    new ConditionalRule<ConnectionSettingsViewModel, LoginOptions>(
        nameof(Username),
        "Username: Cannot be empty, starting with a space or a backslash.",
        x => !string.IsNullOrWhiteSpace(x.Username),
        LoginOptions.Instrument
    )
);

请注意,我没有检查代码,因此您可能会遇到一些编译时问题,但是通常的想法是使用继承从Rule<T>中创建特定规则。这样,每个规则都将很好地适合RuleCollection<T>