2

我现有的班级结构。

AbstractTradeBaseClass -> AbstractTradeClass -> ConcreteTradeClass。

我正在使用 DataAnnotations 和 IDataErrorInfo 在我的 WPF 应用程序中验证我的模型。我已将 IDataErrorInfo 方法移至 AbstractTradeBaseClass,以便所有从基类继承的类都可以使用我。

这是通过使用 Linq 动态读取属性来完成的。

AbstractTradeBaseClass.cs

 public abstract class TradeBaseModel<T> : INotifyPropertyChanged, IDataErrorInfo
{
    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    public void OnPropertyChanged( string propertyName )
    {
        if ( PropertyChanged != null )
        {
            PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
        }
    }

    #endregion

    # region Getter/Validator members for Annotations
    private static readonly Dictionary<string, Func<T, object>>
        propertyGetters = typeof( T ).GetProperties()
                          .Where( p => GetValidations( p ).Length != 0 )
                          .ToDictionary( p => p.Name, p => GetValueGetter( p ) );

    private static readonly Dictionary<string, ValidationAttribute[]> validators =
        typeof( T ).GetProperties()
        .Where( p => GetValidations( p ).Length != 0 )
        .ToDictionary( p => p.Name, p => GetValidations( p ) );

    private static ValidationAttribute[] GetValidations( PropertyInfo property )
    {
        return ( ValidationAttribute[] )property.GetCustomAttributes( typeof( ValidationAttribute ), true );
    }

    private static Func<T, object> GetValueGetter( PropertyInfo property )
    {
        var instance = Expression.Parameter( typeof( T ), "i" );
        var cast = Expression.TypeAs( Expression.Property( instance, property ), typeof( object ) );
        return ( Func<T, object> )Expression.Lambda( cast, instance ).Compile();
    }
    # endregion

    #region IDataErrorInfo Members

    public string Error
    {
        get
        {
            var errors = from i in validators
                         from v in i.Value
                         where !v.IsValid( propertyGetters[i.Key]( **( T )ModelToValidate()** ) )
                         select v.ErrorMessage;
            return string.Join( Environment.NewLine, errors.ToArray() );
        }
    }

    protected Dictionary<string, string> _errors = new Dictionary<string, string>();
    public IDictionary<string, string> Errors
    {
        get { return _errors; }
    }

    public string this[string columnName]
    {
        get
        {
            string errorMessage = string.Empty;
            this.Errors.Remove( columnName );

            //string errorMessage = string.Empty;
            //switch(columnName)
            //{
            //    case "Range":
            //        if ( Range > 15 )
            //        {
            //            errorMessage = "Out of Range, should be less than 15";
            //        }
            //        break;
            //}

            //return errorMessage;

            if ( propertyGetters.ContainsKey( columnName ) )
            {
                var value = propertyGetters[columnName]( **( T )ModelToValidate()** );
                var errors = validators[columnName].Where( v => !v.IsValid( value ) )
                    .Select( v => v.ErrorMessage ).ToArray();

                string error = string.Join( Environment.NewLine, errors );
                this.OnPropertyChanged( "Error" );

                if ( !string.IsNullOrEmpty( error ) )
                {
                    this.Errors.Add( columnName, error );
                }

                return error;
            }

            return string.Empty;

        }
    }

    public abstract object ModelToValidate(); -- ***Gets the child object here, overridden in the child class to return the child object, -- is there a better way to do this using Generics ??***

    #endregion
}

抽象贸易类

 public abstract class TradeClassBase<T> : TradeBaseModel<T>
{
    public string EmptyString
    {
        get { return string.Empty; }
    }
}

具体贸易类

public class CashFlowTrade : TradeClassBase<CashFlowTrade>
{
    private int range;
    [RangeAttribute(0,10, ErrorMessage="Out of Range, Range should be less than 15")]
    public int Range
    {
        get
        {
            return this.range;
        }
        set
        {
            if ( this.range != value )
            {
                this.range = value;
                this.OnPropertyChanged( "Range" );
            }
        }
    }

    public override object ModelToValidate()
    {
        return this;
    }

}

是否有更好的方法来执行此操作,而不是使用抽象方法并在子类中覆盖它以传递实际的子类对象并将其强制转换为 T 类型。上面代码中以粗体表示的用法。

如果有一种方法可以将子类对象传递给基类,那么我可以使用实际的子类对象本身来执行验证操作。


使用类更新了工作代码 约束 T,并使用如下的类型转换运算符。

    public abstract class TradeBaseModel<T> : INotifyPropertyChanged, IDataErrorInfo
    where T : class

用类型转换替换对抽象方法的调用。

 if ( propertyGetters.ContainsKey( columnName ) )
            {
                var value = propertyGetters[columnName]( this as T );
4

1 回答 1

2

是的...直接使用这个。即使在抽象类中,您也可以访问此引用。唯一不能使用它的地方是静态方法。

编辑:

要强制执行类型检查,您可以在 T 上添加一个约束:

public abstract class TradeBaseModel<T> : INotifyPropertyChanged, IDataErrorInfo where T: TradeBaseModel<T>

编辑2:

总结 Kans 在下面的评论,这还不够:这种转换支持从 T 到基本类型的隐式类型转换,而从基本类型到 T 的转换是必需的。唯一的解决方案似乎是在上面的代码中使用 T as 运算符,为此, T 必须是一个类(如果添加了上面的约束就可以了)。

于 2012-07-12T11:18:14.707 回答