反思:在基础 class 中调用派生的 属性 吸气剂

Reflection: Invoking derived property getters in base class

我 运行 在将我的代码集成到更大的系统中时遇到了一些代码约定问题。下面的例子中,BaseClass和DerivedClass1是之前存在的大系统,DerivedClass2是我的代码,合并前从另一个基class继承而来

public abstract class BaseClass
{
    protected BaseClass()
    {
        foreach (var property in this.GetType().GetProperties())
        {
            var value = property.GetValue(this);
            if (value is PropertyEnvelop)
            {
                (...)
            }
        }
    }
}

public class DerivedClass1 : BaseClass
{
    public PropertyEnvelop MyProperty { get; }
        = new PropertyEnvelop("MyPropertyName", typeof(int), default(int));    
    // this is fine, because property initializers run in reverse order:
    // 1. Derived class property initializers
    // 2. Base class property initializers
    // 3. Base class constructor
    // 4. Derived class constructor
    // see link
}

public class DerivedClass2 : BaseClass
{
    public ComplexItem Item
        { get { return ComputeComplexItem(SimpleItem1, SimpleItem2); } }
    // this is BROKEN, because stuff like SimpleItem1, SimpleItem2
    // and ComputeComplexItem TOTALLY depend on some logic from
    // DerivedClass2 constructor and produce exceptions if the ctor
    // wasn't invoked yet.

    public DerivedClass2()
    {
        // prepare everything to bring DerivedClass2 in a valid state
    }
}

Link

(更具体地说,BaseClass 是 MVVM 的 INotifyPropertyChanged 的​​实现,它想为其通知属性做一些自动连接。)

问题是,这里有什么做法不好? 1) 通过基础 class 中的反射调用派生的 class 成员,或者 2) 属性 getter 和方法依赖于它们的 class' 构造函数已经被调用的假设?我应该在我的所有属性中添加 null 检查和类似的逻辑,还是应该禁用此基本 class 行为(如果我无论如何都不打算在我的代码中的任何地方使用 PropertyEnvelop )?我有一种强烈的感觉,尝试调用尚未完全实例化的实例属性的 getter 是错误的(如上述博客的 second part 所说),但是是否有任何官方指南推荐一个选项其他? (我们有复杂的 ViewModel classes,在 ctors 期间有很多内部逻辑 运行。)

对我来说幸运的是,真正的 BaseClass 有一个标志来禁用这种行为,但它是作为 virtual bool 属性 实现的,继承 classes 只是覆盖它;因此,BaseClass 还在其构造函数中调用其虚拟成员,这(AFAIK)也是一种糟糕的代码实践。

我的公司最终决定他们原来的模式是错误的,他们将更改基础 class 实现。建议是:

1) 如果您使用一些 [WarningAlertThisWillBeInvokedBeforeYourCtorAttribute] 标记要在基本构造函数中自动挂接的所有属性,那么您至少会将 getter 和警告放在一处。在反射中,您可以先检查属性,然后才调用 getter.

2) 如果你不想引入属性(比如本例中引入了auto-hook以尽可能加快开发速度),你至少可以勾选type在调用 getter 之前 属性 的 。然后您可以将警告放入类型摘要中,例如

/// <summary>
/// WARNING! Property of this type should be initialized inline, not in ctor!
/// </summary>
public class PropertyEnvelop
{
    (...)
}

3) 通常,在基 class 构造函数中调用 属性 getter 根本不是一个好主意,因此我们将寻求其他解决方案。