#c#
#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;
}
// ...
}
Когда я теперь хочу добавить my RuleCondition<T>
в a Rule
(который наследуется от Collection<Rule<T>>
и 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)));
Я получаю сообщение об ошибке, что тип не соответствует, что правильно. Теперь моя проблема: как получить значение TValue
для RuleCondition
исключения необходимости объявлять его «сверху» и разбивать весь мой код. Как-то неправильно объявлять ограничение везде только для того, чтобы получить его в RuleCondition<TValue>
классе.
Есть ли более простой способ? Что я упускаю?
РЕДАКТИРОВАТЬ: T
уже имеет тип *ViewModel
.
Я думаю, мне нужно добавить больше объяснений:
Здесь начинается «цепочка»:
public class ConnectionSettingsViewModel : NotifyDataErrorInfo<ConnectionSettingsViewModel>
{ }
Если бы я теперь добавил TValue
ограничение к Rule
классу таким образом Rule<T, TValue>
, мне также нужно предоставить это ограничение NotifyDataErrorInfo
классу. Итак, к «верхнему» классу. Но это кажется неправильным. Чтобы это ограничение было объявлено в каждом классе между the NotifyDataErrorInfo
и «последним» классом RuleCondition
.
РЕДАКТИРОВАТЬ 2: я взял код отсюда, чтобы реализовать проверку элемента управления в моем приложении MVVM. Но я подумал, что мне нужно выполнить некоторые условия для нескольких элементов управления. Так что правило не будет «выполняться» каждый раз, а только в том случае, если проверено определенное RadioButton
, например.
Комментарии:
1. Я думаю
private readonly RuleCondition<TValue> mCondition;
, что должно бытьprivate readonly RuleCondition<T> mCondition;
так, чтобы он использовалT
то, с чем был объявлен класс.2. Не может быть, потому
T
что уже имеет тип*ViewModel
. Мне нужно другое ограничение, чтобы получитьTValue
. Но если я добавлю это, какRule<T, TValue>
я получаю ошибки, которые в нескольких местах я должен указать этот тип. Что имеет смысл. Но кажется неправильным объявлять его во многих местах, просто чтобы получить его вRuleCondition<TValue>
классе.3. Нужно ли вам знать
Type
Value
свойство при обработке условий? Можете ли вы работать сobject
вместо этого?4. @DarjanBogdan Я думал об этом. Но тогда я должен использовать некоторое отражение, чтобы получить тип. Мне нужно иметь по крайней
TValue
мере, бытьenum
и бытьbool
.5. Какова связь между
NotifyDataErrorInfo<ConnectionSettingsViewModel>
иRule<T>
, наследует ли он его?
Ответ №1:
Основываясь на ваших данных и этом сообщении в блоге, вы можете сделать что-то вроде этого. Примите во внимание, что вам нужно будет немного адаптировать классы, представленные в этом блоге.
Во-первых, вы можете создать ConditionalRule<T, TValue>
то, которое наследует DelegateRule<T>
(или любое другое Rule<T>
, которое вы хотите). Для этого вам нужно будет распечатать DelegateRule
class
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>
Комментарии:
1. Спасибо, Дарьян, проверю это!
2. Бодган, мне пришлось немного его подправить. Но нет, я заставил это работать. Правило будет применено только в том случае, если выполнено условие. Еще раз спасибо!
3. @FredM Рад это слышать! 🙂