【问题标题】:C# class design - instance independent informationC# 类设计 - 实例独立信息
【发布时间】:2016-07-04 01:11:06
【问题描述】:

我遇到了类设计难题:我有一个通用基类和多个派生类。基础是一个提供通用功能的CRTP 类,其中一些需要继承类特定的数据。因此,基类是抽象的,子类必须实现某些“提供者”方法。例如:

public abstract class Base<T> where T : Base<T>, new() {
    private static T reference = new T();

    public static int Foo => reference.GetFoo();

    public static string Bar => reference.GetBar();

    protected abstract int GetFoo();

    protected abstract string GetBar();

    // ...
}

问题是信息不是特定于实例的,应该可以从通用参数访问。所以使用上面的例子,这个方法可以在另一个类中:

public void ComputeSomething<T>() where T : Base<T>, new() {
    int foo = Base<T>.Foo;

    // ...
}

或者如果子类已知:ChildClass.Foo

这可行,但总体而言,该解决方案感觉“肮脏”,因为我讨厌用类型信息来混乱每个实例,并且基类必须保留一个 reference 实例(如何在其基类中创建子类仍然有点弯曲我的大脑,通常看起来是个坏主意)。我会将信息放在缓存或工厂或其他东西中,但我无法控制所有子类,因此系统必须是可扩展的。我研究了使用属性,但是(据我所知)没有办法强制在编译时存在某些属性。真的,我觉得我需要静态接口或静态抽象成员,但 C# 没有这些。

所以我的问题是:这种问题一般是怎么解决的?

【问题讨论】:

  • 您是否能够更改/重构使用静态成员的代码以仅使用抽象成员?通常,在可测试性/控制反转/依赖注入盛行的世界中,您会寻求让所有代码都利用非静态成员,以便它们很容易被替换或模拟......
  • 关于您的陈述“如何在其基类中创建子类仍然让我有点不知所措,而且通常看起来是个坏主意” - 是的!你是对的,坏主意。在构建依赖等方面,您会严重限制自己。
  • 是的,我当然可以重构代码,反正它更像是一个原型。在很多地方(如示例中),这会导致类似new T().Foo 的内容,这似乎不太正确。我开始认为我需要一个单独的提供程序类或其他东西

标签: c# generics class-design crtp


【解决方案1】:

我开始认为我需要一个单独的提供程序类或其他东西

这就是答案。每当我遇到这些复杂的通用继承场景时,问题是我通过继承而不是组合来组合类。这就是为什么你会听到原理“favor composition over inheritance.

派生类需要从基类中得到什么,是真的需要通过继承来获得,还是可以通过创建一个单独的类并依赖它来获得?

如果多个派生类将依赖于同一个类(这暗示了继承的情况),那么也许可以将单独的类依赖关系构建到基类中。但它仍然是一个单独的类。派生类继承对它的依赖,而不是继承实际的方法和属性。

我发现继承在重构的结果中更常见,也许是重构重复代码的一种方法(或在它开始之前停止它。)当我开始认为我要重用它时前面的代码很可能最终变得过于复杂。也许是因为我们仍然在编写类并确定它们将如何工作。试图建立一个层次结构迫使我们预先假设太多,然后放弃设计是令人沮丧的。它在事后重构或我们考虑添加可能重复代码的新类时效果更好。

但即使在我添加新类并且不想重复代码的情况下,我也可能会考虑将重复的代码重构到自己的类中,而不是让新类继承现有的类。

【讨论】:

  • 谢谢!这正是我需要听到的。我想我只是看代码太久了......今天我读了这个,移动了一些功能,制作了一些接口等等,整个事情几乎都理顺了。无论如何,谢谢!
  • 太棒了。我一遍又一遍地将自己描绘成奇怪的通用继承角落,所以当有人向我解释替代方案时它真的很有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多