【问题标题】:Override property or calculate property in constructor在构造函数中覆盖属性或计算属性
【发布时间】:2013-03-22 09:19:37
【问题描述】:

例如,我有一个基类,我需要一个属性 将在派生类中计算。我有两个变体(SomeProperty1 和 SomeProperty2):

public class BaseClass
{
    public int SomeProperty1{get;set;}
    public override int SomeProperty2{get;set;}
}

public class DerivedClass : BaseClass
{
    public DerivedClass()
    {
       SomeProperty1 = 100;
    }
    public override int SomeProperty2
    {
        get
        {
            return 100;
        }
    }
}

问题是最好的方法是什么,SomeProperty1 或 SomeProperty2?

【问题讨论】:

  • 算什么?
  • 这取决于您计划如何进一步使用属性。如果您确定不应更改属性值 - 那么更好的解决方案将类似于 SomeProperty2。如果计划更改属性值 - 那么SomeProperty1 的变体更适合。
  • 您说“计算”,但您返回一个常数。如果你真的计算了这个值并且它可能是一个相当复杂的计算,那么在构造函数中只做一次似乎比每次访问属性时都计算更好。

标签: c# .net inheritance polymorphism virtual


【解决方案1】:

向基类添加一个名为CalcSomeProperty() 的受保护抽象方法。

然后根据CalcSomeProperty() 实现您的属性。这将强制派生类实现它。

例如:

public abstract class BaseClass
{
    public int SomeProperty
    {
        get
        {
            return CalcSomeProperty();
        }
    }

    protected abstract int CalcSomeProperty();
}

或者,您可以使属性本身抽象:

public abstract class BaseClass
{
    public abstract int SomeProperty { get; }
}

在任何一种情况下,您都在强制派生类实现属性计算。

将计算分离到一个受保护的方法(而不是使用更简单的抽象属性)的一个优点是,如果计算很慢,您可以在具体的属性实现中执行缓存:

public abstract class BaseClass
{
    protected BaseClass()
    {
        _someProperty = new Lazy<int>(CalcSomeProperty);
    }

    public int SomeProperty
    {
        get
        {
            return _someProperty.Value;
        }
    }

    protected abstract int CalcSomeProperty();

    private readonly Lazy<int> _someProperty;
}

【讨论】:

    【解决方案2】:

    这在很大程度上取决于计算类型。如果它需要很长时间并且在对象的生命周期内没有改变,那么通过将它添加到属性中会浪费计算时间。所以在那种情况下,我肯定会保持你的代码干净并在你的构造函数中初始化属性。

    如果是一个常量值,把它保存在构造函数中会更清楚。您的构造函数反映了您的对象的外观。

    显然,如果计算是动态的,则您需要在属性中包含该部分。

    注意:如果你有一个虚拟属性并且你在你的构造函数中初始化它,那么如果你的类不是sealed,你会引入一个警告。这背后的“危险”在this thread 中比我能解释得更好。

    【讨论】:

      【解决方案3】:

      选项1主要用于用例,当属性应该初始化一次时,初始化很容易和微不足道。

      选项 2 允许控制初始化流程(例如,进行延迟初始化)。反过来,初始化流程取决于属性性质。

      【讨论】:

        【解决方案4】:

        如果您真的是在子类中覆盖,那么也许您希望属性是虚拟的:

        public virtual int SomeProperty2{get;set;}
        

        虽然我最好在基类中声明一个公共属性和一个可以在子类中覆盖的受保护虚拟属性:

        // base
        protected virtual int SomePropertyInternal2
        {
            get
            {
                return 10;
            }
        }
        
        public int SomeProperty2
        {
            get
            {
                return SomePropertyInternal2;
            }
        
        // child
        protected override int SomePropertyInternal2
        {
            return 100;
        }
        

        在这种情况下,您将覆盖内部实现,而公共合同保持不变。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2013-08-01
          • 1970-01-01
          • 2020-12-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-19
          相关资源
          最近更新 更多