使用泛型访问基本抽象类方法中的子类对象

时间:2012-07-12 11:12:39

标签: c# .net wpf

My Existing类结构。

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
}

AbstractTradeClass

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

ConcreteTradeClass

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. 上面的代码在BOLD中显示用法。

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


更新了工作代码 使用类约束T并使用类型转换操作符,如下所示。

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

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

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

1 个答案:

答案 0 :(得分:2)

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

修改

要强制执行类型检查,您可以在T:

上添加约束
public abstract class TradeBaseModel<T> : INotifyPropertyChanged, IDataErrorInfo where T: TradeBaseModel<T>

编辑2:

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