C# 静态成员“继承” - 为什么会有这个存在?

发布于 2024-08-22 02:19:08 字数 1025 浏览 5 评论 0原文

在 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 的子类,您仍然无法调用 TM() (这可能会简化 VM 的操作),更不用说编写不同的 M在子类中实现并使用它。

因此,静态成员的“继承”看起来只是命名空间污染;即使您明确限定名称(即 BM),A 的版本仍然可以解析。

编辑与命名空间比较:

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 的版本;如果您尝试这样做,编译器确实会报告错误。相比之下,如果我显式引用 BM(),为什么编译器不报告错误?毕竟,“B”中没有“M”方法……

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

In C#, a superclass's static members are "inherited" into the subclasses scope. For instance:

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()

Now, you can't inherit static classes, and the only place I can imagine that static inheritance might matter ignores it entirely: although you can make a generic constraint that requires a type parameter T to be a subclass of A, you still cannot call T.M() (which probably simplifies things for the VM), let alone write a different M implementation in a subclass and use that.

So, the "inheritance" of static members merely looks like namespace pollution; even if you explicitly qualify the name (i.e. B.M) A's version is still resolved.

Edit compare with namespaces:

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

Within N1.N2.N3 It makes sense that if I use X without qualification it refers to N1.N2.X. But if I explicitly refer to N1.N2.N3.X - and no such class exists - I don't expect it to find N2's version; and indeed to compiler reports an error if you try this. By contrast, if I explicitly refer to B.M(), why doesn't the compiler report an error? After all, there's no "M" method in "B"...

What purpose does this inheritance have? Can this feature be used constructively somehow?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

送你一个梦 2024-08-29 02:19:08

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

没错,只是一个人的污染是另一个人添加了辛辣调味品。

我认为 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# 的数量非常少。

So, the "inheritance" of static
members merely looks like namespace
pollution

That's right, except that one guy's pollution is another guy's added spicy flavouring.

I think Martin Fowler, in his work on DSLs, has suggested using inheritance in this way to allow convenient access to static methods, allowing those methods to be used without class name qualification. So the calling code has to be in a class that inherits the class in which the methods are defined. (I think it's a rotten idea.)

In my opinion, static members should not be mixed into a class with a non-static purpose, and the issue you raise here is part of the reason why it's important not to mix them.

Hiding private static mutable data inside the implementation of an otherwise "instancey" class is particularly horrible. But then there are static methods, which are even worse mixers. Here's a typical use of static methods mixed into a class:

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();
    }
}

It's the static factory method pattern. It's pointless most of the time, but even worse is that now we have this:

public class AnotherThing : Thing { }

This now has a static Make method which returns a Thing, not a AnotherThing.

This kind of mismatch strongly implies that anything with static methods should be sealed. Static members fail to integrate well with inheritance. It makes no sense to have them heritable. So I keep static things in separate static classes, and I gripe about redundantly having to declare every member static when I've already said that the class is static.

But it's just one of those too-late-now things. All real, working languages (and libraries, and products) have a few of them. C# has remarkably few.

是伱的 2024-08-29 02:19:08

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

使用智能感知时,您可以自动知道该类可用的每个静态成员。

当然,它们不是继承的,只是一个捷径

I rather have access to all my based static members in derived classes.
Otherwise i would need to know exactly where the static member was defined and call it explicitly.

When using Intellisense you can automatically know every static member available to that kind of class.

Of course, they are not inherited, it's just a shortcut

千寻… 2024-08-29 02:19:08

这就是它的工作原理,在大多数情况下可能只是一个愚蠢的答案。但在这种情况下,它就是这样运作的;因为你从 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,按设计。并且重写静态实现的方法没有多大意义,因为它只是子类中的访问器。

只能有一个。覆盖它意味着该子类有一个新的实现,这将消除术语“静态”。

That's how it works, would probably just be a stupid answer in most cases. But in this case, it is how it works; since you derive from A you say that you are A + the extra features you add.

Therefore you need to be able to access the same variables that you would through an instance of A.

However, inheriting a static class makes no sense while access to the static members / fields / methods does.

An example of this is the following:

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; }
}

Where the test looks like this:

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

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

Both of the WriteLines outputs "Second Test", this is because both BaseUser and User should use DefaultUserPool, by design. And overriding static implemented methods wouldn't make mucn sense since it's just an accessor in the child-class.

There can be only one. Overriding it would mean that there's a new implementation for that sub-class, which would kill the term "static".

活泼老夫 2024-08-29 02:19:08

实际上,据我了解,这只是编译器提供的一个快捷方式。语法糖。 BM() 只会编译为 AM(),因为 B 没有 static M() 而 A 有。就是为了方便写,没有别的。不存在“静态继承”。

补充:“重新定义”时对new的要求只是为了避免你不小心搬起石头砸自己的脚。

Actually, as I understand it, this is just a shortcut provided by the compiler. Syntax sugar. B.M() will just compile to A.M() since B does not have a static M() and A does. It's for easier writing, nothing else. There is no "static inheritance".

Added: And the requirement for new when "redefining" is just so that you don't accidentally shoot yourself in the foot.

想念有你 2024-08-29 02:19:08

我认为它用于访问基类的受保护静态成员。

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

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

I think it's for accessing protected static members of the base class.

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

class Subclass : Base
{
   public void Run()
    {
       Helper("From the subclass");
    }
}
傲影 2024-08-29 02:19:08

那么...还有什么选择呢?

问题提到...

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

但是“B”中派生的“M”方法班级。

如果编译器没有为程序员提供用于基本情况的统一虚拟表,那么程序员将不得不遍历基本类型来查找静态方法。这会破坏多态性

维基百科...

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

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

So... What's the alternative?

The question mentions...

why doesn't the compiler report an error? After all, there's no "M" method in "B"...

But there is a derived "M" method in "B" class.

If the compiler did not present the programmer a unified virtual table for base cases, then the programmer would have to go hunting through base types to find static methods. This would break polymorphism.

Wikipedia...

Subtype polymorphism, almost universally called just polymorphism in the context of object-oriented programming, is the ability of one type, A, to appear as and be used like another type, B....

In strongly typed languages, polymorphism usually means that type A somehow derives from type B, or type C implements an interface that represents type B.

孤君无依 2024-08-29 02:19:08

我总是认为这是一种通过继承类来防止您希望为所有子类保留相同功能的项目的任何形式的多态性的方法。

出于某种原因忽略上述内容,我正在考虑密封而不是静态

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

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

I always see it a means of preventing any form of polymorphism by the inheriting class on those items that you wish to retain the same function for all child classes.

ignore the above for some reason I was thinking of sealed instead of static

I suppose that you'd use static member variables and functions in order to ensure that any data or functionallity is not dependent on the a class instance as it would be instantiated only the once.

An example of use would be say a counter value that would keep a live count of all instances of a superclass's subclasses (each subclass increments the static count value on construction). This count value would be available and equal for all instances of the subclass.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文