C#静态成员“继承” - 为什么会存在这种情况?

时间:2010-02-17 15:11:57

标签: c# inheritance static language-design

在C#中,超类的静态成员被“继承”到子类范围中。例如:

class A { public static int M() { return 1; } }
class B : A {}
class C : A { public new static int M() { return 2; } }
[...]
A.M(); //returns 1
B.M(); //returns 1 - this is equivalent to A.M()
C.M(); //returns 2 - this is not equivalent to A.M()

现在,你不能继承静态类,我唯一​​可以想象静态继承可能重要的地方完全忽略它:尽管你可以创建一个通用约束,它需要一个类型参数T作为子类在A中,您仍然无法调用T.M()(这可能会简化VM的内容),更不用说在子类中编写不同的M实现并使用它。

因此,静态成员的“继承”仅仅看起来像命名空间污染;即使您明确限定了该名称(即B.MA的版本仍然已解决。

编辑 与命名空间进行比较:

namespace N1{  class X();   }
namespace N1.N2 {  class X();   }
namespace N1.N2.N3 { [...] }

N1.N2.N3内有意义的是,如果我使用X而没有限定,则会引用N1.N2.X。但是,如果我明确提到N1.N2.N3.X - 并且不存在这样的类 - 我不希望它找到N2的版本;实际上,如果您尝试这样做,编译器会报告错误。相比之下,如果我明确引用B.M(),为什么编译器不报告错误?毕竟,“B”中没有“M”方法......

这种遗产的目的是什么?可以以某种方式建设性地使用此功能吗?

7 个答案:

答案 0 :(得分:25)

  

所以,静态的“继承”   成员只看起来像命名空间   污染

这是对的,除了一个人的污染是另一个人加入辛辣调味品。

我认为Martin Fowler在他的DSL工作中建议以这种方式使用继承来方便地访问静态方法,允许在没有类名认证的情况下使用这些方法。因此,调用代码必须位于继承定义方法的类的类中。 (我认为这是一个糟糕的主意。)

在我看来,静态成员不应该被混合到一个具有非静态目的的类中,你在这里提出的问题是为什么重要的是不要混合它们的部分原因。

在其他“实例”类的实现中隐藏私有静态可变数据尤其可怕。但是有静态方法,甚至是更糟糕的混音器。以下是混合到类中的静态方法的典型用法:

public class Thing
{
    // typical per-instance stuff
    int _member1;
    protected virtual void Foo() { ... }
    public void Bar() { ... }

    // factory method
    public static Thing Make()
    {
        return new Thing();
    }
}

这是静态工厂方法模式。大部分时间都没有意义,但更糟糕的是现在我们有了这个:

public class AnotherThing : Thing { }

现在有一个静态Make方法,它返回Thing,而不是AnotherThing

这种不匹配强烈暗示任何采用静态方法的东西都应该密封。静态成员无法与继承很好地集成。将它们遗传是没有意义的。因此,我将静态事物保存在单独的静态类中,并且当我已经说过该类是静态的时,我抱怨冗余地必须声明每个成员静态。

但这只是其中一件太迟了的事情。所有真正的工作语言(以及图书馆和产品)都有一些。 C#的数量非常少。

答案 1 :(得分:10)

我宁愿访问派生类中所有基于静态的成员。 否则,我需要确切地知道静态成员的定义,并明确地调用它。

使用Intellisense时,您可以自动了解该类所有可用的静态成员。

当然,它们不是遗传的,它只是一个捷径

答案 2 :(得分:8)

这就是它的工作方式,在大多数情况下可能只是一个愚蠢的答案。但在这种情况下,它是如何工作的;因为你是从A派生的,所以你说你是A +你添加的额外功能。

因此,您需要能够访问与A实例相同的变量。

但是,在访问静态成员/字段/方法时,继承静态类是没有意义的。

以下是一个例子:

internal class BaseUser
{
    public static string DefaultUserPool { get; set; }
}
internal class User : BaseUser
{
    public int Id { get; set; }
    public string Name { get; set; }
    public User Parent { get; set; }
}

测试如下:

User.DefaultUserPool = "Test";
BaseUser.DefaultUserPool = "Second Test";

Console.WriteLine(User.DefaultUserPool);
Console.WriteLine(BaseUser.DefaultUserPool);

两个WriteLines输出“Second Test”,这是因为BaseUser和User都应该使用DefaultUserPool, by design 。并且重写静态实现的方法不会使mucn有意义,因为它只是子类中的一个访问器。

只能有一个。覆盖它意味着该子类有一个新的实现,它会杀掉术语“静态”。

答案 3 :(得分:5)

实际上,据我所知,这只是编译器提供的快捷方式。语法糖。 B.M()只会编译为A.M(),因为B没有static M()而A有。{1}}这是为了更容易写作,没有别的。没有“静态继承”。

已添加:“重新定义”时对new的要求只是为了让您不会意外射击自己。

答案 4 :(得分:0)

我总是认为这是一种通过继承类来阻止任何形式的多态的方法,这些类希望为所有子类保留相同的函数。

因为某些原因忽略了上述内容我认为是密封而不是静态

我认为您使用静态成员变量和函数是为了确保任何数据或函数都不依赖于类实例,因为它只会被实例化一次。

使用的一个例子是一个计数器值,它将保持超类的子类的所有实例的实时计数(每个子类在构造时增加静态计数值)。对于子类的所有实例,此计数值都可用且相等。

答案 5 :(得分:0)

我认为这是为了访问基类的受保护的静态成员。

class Base
{
    protected static void Helper(string s)
    {
       Console.WriteLine(s);
    }
}

class Subclass : Base
{
   public void Run()
    {
       Helper("From the subclass");
    }
}

答案 6 :(得分:0)

那么...有什么替代方案?

问题提到......

  

为什么编译器报告错误?毕竟,“B”中没有“M”方法......

“B”类中的派生“M”方法

如果编译器没有为程序员提供基本案例的统一虚拟表,那么程序员就必须通过基类找到静态方法。这会打破polymorphism

...百科

  

子类型多态,在面向对象编程的上下文中几乎普遍称为多态,是一种类型A能够像另一种类型一样出现和使用的能力,B ....

     

在强类型语言中,多态通常意味着类型A以某种方式从类型B派生,或者类型C实现表示类型B的接口。