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

Eam*_*nne 45 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()
Run Code Online (Sandbox Code Playgroud)

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

因此,静态成员的"继承"仅仅看起来像命名空间污染; 即使你明确限定名称(即B.M)A的版本仍然解决.

编辑与命名空间的比较:

namespace N1{  class X();   }
namespace N1.N2 {  class X();   }
namespace N1.N2.N3 { [...] }
Run Code Online (Sandbox Code Playgroud)

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

这种继承有什么作用?这个功能可以以某种方式建设性地使用吗?

Dan*_*ker 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

public class AnotherThing : Thing { }
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • "一个人的命名空间污染是另一个人添加的辛辣调味品".我想这可能是我有史以来最喜欢的一句话. (6认同)

Lui*_*ipe 10

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

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

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


Fil*_*erg 8

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

因此,您需要能够访问通过A实例的相同变量.

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

这方面的一个例子如下:

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; }
}
Run Code Online (Sandbox Code Playgroud)

测试看起来像这样:

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

Console.WriteLine(User.DefaultUserPool);
Console.WriteLine(BaseUser.DefaultUserPool);
Run Code Online (Sandbox Code Playgroud)

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

只可以有一个人.覆盖它意味着该子类有一个新的实现,这将杀死术语"静态".

  • 问题是为什么User.DefaultUserPool会编译.据我所知,这只是令人困惑; 例如,正如您的示例所指出的那样,乍一看这两个不同的静态引用是否引用相同的基础变量并不明显.如果User拥有它自己的DefaultUserPool(在另一个程序集中,或者使用new关键字),这个代码也会在没有警告的情况下编译但导致不同的输出 - 简而言之,你*可以*有效地覆盖一个静态成员(即使,对于静态案例,重写和隐藏是等效的). (3认同)

Vil*_*lx- 5

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

补充:并且new"重新定义" 的要求只是为了让你不小心射击自己.