C#虚拟(virtual)自动属性使用时的一点注意事项

        C#3.0及以后版本中提供的自动属性,的确省了几行代码,程序看起来也优雅不少。但最近在使用一个虚拟(virtual)自动属性时,发现了一个怪异现象,见如下代码:

    public class Program
    {
        static void Main(string[] args)
        {
            TErrorEx er = new TErrorEx();
            er.Do();
        }
    }

    public class TError
    {
        public virtual string ErrorMessage { get; set; }  // virtual自动属性 
        public virtual bool HasError
        {
            get 
            {
                Console.WriteLine("base.HasError called.");
                Console.ReadKey();
                return !string.IsNullOrEmpty(this.ErrorMessage);  // 去掉this现象依旧,该this将指向派生类override的ErrorMessage
            }
        }
    }

    public class TErrorEx : TError
    {
        public override string ErrorMessage  // 重写基类自动属性
        {
            get
            {
                Console.WriteLine("this.ErrorMessage called.");
                Console.ReadKey();
                if (base.HasError == true)  // 调用基类属性,此处产生递归调用
                {
                    return base.ErrorMessage;
                }
                return null;
            }
        }

        public void Do()
        {
            if (base.HasError == true)  // 使用this.HasError, 仍然将调用 base.HasError
            {
                return;
            }
        }
    }

        上述代码中定义了基类TError及其派生类TErrorEx,并在派生类中重写(override)了基类ErrorMessage属性。此时,调用派生类TErrorEx的Do()方法,该方法将调用基类属性base.HasError。从输出看,信息流如下:

  • base.HasError called. :直接调用基类属性。
  • this.ErrorMessage called.:基类属性返回调用派生类的属性,这个是递归调用的问题所在!
  • base.HasError called.:派生类的ErrorMessage中又调用了基类的属性。

        当时困惑的一点是,为何在调用基类的 !string.IsNullOrEmpty(this.ErrorMessage)语句时返回调用派生类的ErrorMessage属性?!

        解决的方法有二:

        1)在派生类修改代码为改写基类属性形式,即

        public new string ErrorMessage  // new 改写基类属性
        {
            get
            {
                Console.WriteLine("this.ErrorMessage called.");
                Console.ReadKey();
                if (base.HasError == true)
                {
                    return base.ErrorMessage;
                }
                return null;
            }
        }
        2)修改基类自动属性为普通属性,即
        private string errorMessage;
        public string ErrorMessge
        {
            get { return this.errorMessage; }
            set { this.errorMessage = value; }
        }

        public virtual bool HasError
        {
            get
            {
                Console.WriteLine("base.HasError called.");
                Console.ReadKey();
                return !string.IsNullOrEmpty(this.errorMessage);  // 实体属性
            }
        }
        仔细分析还是发现了问题所在:是著名的多态造成的!在调用基类的 !string.IsNullOrEmpty(this.ErrorMessage)时,因为ErrorMessage有重写(属性等价于方法),所以就调用了其重写的属性了。
        写代码时一般注意方法的多态,往往忽略属性的多态,特别是这种自动实现的属性,重写时要特别注意。




你可能感兴趣的:(.NET实现与应用技术)