c# - 反射:在基类中调用派生属性 getter
问题描述
在将代码集成到更大的系统中时,我遇到了一些代码约定问题。在下面的示例中,BaseClass 和 DerivedClass1 是以前存在的大系统,而 DerivedClass2 是我的代码,它是在合并之前从另一个基类继承而来的。
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
}
}
(更具体地说,BaseClass 是 MVVM 的 INotifyPropertyChanged 的实现,它希望对其通知属性进行一些自动连接。)
问题是,这里的什么做法是不好的?1) 通过基类中的反射调用派生类成员,或者 2) 依赖于其类的构造函数已被调用的假设的属性获取器和方法?我应该在我的所有属性中添加空检查和类似的逻辑,还是应该禁用这个基类行为(如果我无论如何都不打算在我的代码中的任何地方使用 PropertyEnvelop)?我有一种强烈的感觉,尝试调用尚未完全实例化的实例的属性的 getter 是错误的(正如提到的博客的第二部分所说),但是是否有任何官方指南推荐一个选项而不是另一个选项?(我们有复杂的 ViewModel 类,其中有很多内部逻辑要在 ctor 期间运行。)
对我来说幸运的是,真正的 BaseClass 有一个标志来禁用此行为,但它是作为虚拟 bool 属性实现的,继承的类只是覆盖它;因此,BaseClass 还在其构造函数中调用其虚拟成员,这(AFAIK)也是一种不好的代码实践。
解决方案
我公司最后决定他们原来的模式是错误的,他们将改变基类的实现。建议是:
1) 如果你用一些 [WarningAlertThisWillBeInvokedBeforeYourCtorAttribute] 标记你想在基础构造函数中自动挂钩的所有属性,那么你至少会在一个地方拥有 getter 和警告。在反射中,您可以首先检查属性,然后才调用 getter。
2)如果你不想引入属性(在这种情况下,引入了自动钩子以尽可能地加快开发速度),你至少可以在调用getter之前检查属性的类型。然后您可以将警告放入类型摘要中,例如
/// <summary>
/// WARNING! Property of this type should be initialized inline, not in ctor!
/// </summary>
public class PropertyEnvelop
{
(...)
}
3) 通常,在基类ctor 中调用属性getter 根本不是一个好主意,所以我们将寻求另一种解决方案。
推荐阅读
- android - 从 Fire Base (FCM) 批量发送组 ID 推送通知?
- javascript - package.json 中包的版本之前为 False
- nginx - 我在这个 proxy_pass Nginx + Tomcat 7 中做错了什么
- angular - 将文本从一个组件传递到 Angular 中的另一个组件
- java - Spring:如何使服务以 PlatformTransactionManager bean 的可用性为条件?
- ios - 每次移动到不同的视图控制器时都会加载 Firestore 数据
- kubernetes - 多条路径的入口规则未按预期工作
- c# - 将多个从 XML 创建的属性和/或类转换为 Visual Studio 中的自动属性
- haskell - 对包含单个元素和列表的列表进行 Fmap
- xcode - Xcode:无法加载 Info.plist 异常 (eGPUOverrides)