2

私の既存のクラス構造。

AbstractTradeBaseClass - > AbstractTradeClass -> ConcreteTradeClass.

DataAnnotations と IDataErrorInfo を使用して、WPF アプリケーションでモデルを検証しています。基本クラスから継承するすべてのクラスで i を使用できるように、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 に答える