为什么我不能继承静态类?

时间:2009-04-21 19:23:38

标签: c# inheritance static

我有几个不需要任何状态的课程。从组织的角度来看,我想把它们放到层次结构中。

但似乎我不能为静态类声明继承。

类似的东西:

public static class Base
{
}

public static class Inherited : Base
{
}

无效。

为什么语言的设计者会关闭这种可能性?

12 个答案:

答案 0 :(得分:164)

来自here的引文:

  

这实际上是设计的。似乎没有充分的理由继承静态类。它有公共静态成员,您始终可以通过类名本身访问它们。 我看到继承静态内容的唯一原因是糟糕的,例如保存几个字符的输入。

     

可能有理由考虑将静态成员直接引入范围的机制(事实上我们将在Orcas产品周期之后考虑这一点),但静态类继承不是要走的路:它是错误的使用机制,仅适用于碰巧驻留在静态类中的静态成员。

     

(Mads Torgersen,C#语言PM)

channel9

的其他意见
  

.NET中的继承仅适用于实例库。静态方法是在类型级别上定义的,而不是在实例级别上。这就是为什么覆盖不适用于静态方法/属性/事件......

     

静态方法只在内存中保存一次。没有为它们创建的虚拟表等。

     

如果在.NET中调用实例方法,则始终为其提供当前实例。这是.NET运行时隐藏的,但它确实发生了。 每个实例方法都有第一个参数,指向运行该方法的对象的指针(引用)。静态方法不会发生这种情况(因为它们是在类型级别定义的)。编译器应如何决定选择要调用的方法?

     

(littleguru)

作为一个有价值的想法, littleguru 对此问题有部分“解决方法”:Singleton模式。

答案 1 :(得分:70)

您不能继承静态类的主要原因是它们是抽象的和密封的(这也可以防止创建它们的任何实例)。

所以这个:

static class Foo { }

汇编到这个IL:

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }

答案 2 :(得分:22)

以这种方式思考:通过类型名称访问静态成员,如下所示:

MyStaticType.MyStaticMember();

如果您继承该类,则必须通过新类型名称访问它:

MyNewType.MyStaticMember();

因此,当在代码中使用时,新项目与原始项目没有关系。对于像多态这样的东西,没有办法利用任何继承关系。

也许您认为您只想扩展原始课程中的一些项目。在这种情况下,没有什么可以阻止您只使用全新类型的原始成员。

也许您想要将方法添加到现有的静态类型中。你可以通过扩展方法做到这一点。

也许您希望能够在运行时将静态Type传递给函数并调用该类型的方法,而不知道该方法的确切功能。在这种情况下,您可以使用接口。

所以,最后你并没有从继承静态类中获得任何东西。

答案 3 :(得分:3)

只需通过命名空间即可实现使用类层次结构实现的目标。因此,支持namespapces的语言(如C#)将不会使用实现静态类的类层次结构。由于您无法实例化任何类,因此您只需要通过使用命名空间获得的类定义的分层组织

答案 4 :(得分:3)

您可以使用合成代码...这将允许您从静态类型访问类对象。但仍然无法实现接口或抽象类

答案 5 :(得分:2)

虽然您可以通过继承的类名访问“继承的”静态成员,但静态成员并不是真正继承的。这部分是为什么它们不能是虚拟的或抽象的,也不能被覆盖。在您的示例中,如果您声明了Base.Method(),则编译器无论如何都会将对Inherited.Method()的调用映射回Base.Method()。您也可以明确地调用Base.Method()。您可以编写一个小测试并使用Reflector查看结果。

那么......如果你不能继承静态成员,并且静态类只能包含 静态成员,那么继承静态类会有什么好处呢?

答案 6 :(得分:1)

嗯...如果你只是用静态方法填充非静态类,它会有很大不同吗?

答案 7 :(得分:1)

你可以做一些看似静态继承的事情。

这是诀窍:

public abstract class StaticBase<TSuccessor>
    where TSuccessor : StaticBase<TSuccessor>, new()
{
    protected static readonly TSuccessor Instance = new TSuccessor();
}

然后你可以这样做:

public class Base : StaticBase<Base>
{
    public Base()
    {
    }

    public void MethodA()
    {
    }
}

public class Inherited : Base
{
    private Inherited()
    {
    }

    public new static void MethodA()
    {
        Instance.MethodA();
    }
}

Inherited类本身不是静态的,但我们不允许创建它。它实际上继承了构建Base的静态构造函数,Base的所有属性和方法都可以作为静态构建。现在,唯一要做的就是为每个需要向静态上下文公开的方法和属性创建静态包装器。

需要手动创建静态包装器方法和new关键字。但是这种方法有助于支持与静态继承非常相似的东西。

P.S。 我们使用它来创建编译查询,这实际上可以用ConcurrentDictionary替换,但是一个静态只读字段及其线程安全性已经足够了。

答案 8 :(得分:1)

您可以做的解决方法不是使用静态类,而是隐藏构造函数,因此类静态成员是类外可访问的唯一内容。结果是一个可继承的&#34;静态&#34;基本上是这样的:

public class TestClass<T>
{
    protected TestClass()
    { }

    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public class TestClass : TestClass<double>
{
    // Inherited classes will also need to have protected constructors to prevent people from creating instances of them.
    protected TestClass()
    { }
}

TestClass.Add(3.0, 4.0)
TestClass<int>.Add(3, 4)

// Creating a class instance is not allowed because the constructors are inaccessible.
// new TestClass();
// new TestClass<int>();

不幸的是因为&#34; by-design&#34;语言限制我们无法做到:

public static class TestClass<T>
{
    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public static class TestClass : TestClass<double>
{
}

答案 9 :(得分:0)

静态类和类成员用于创建可在不创建类实例的情况下访问的数据和函数。静态类成员可用于分离独立于任何对象标识的数据和行为:无论对象发生什么,数据和函数都不会更改。当类中没有依赖于对象标识的数据或行为时,可以使用静态类。

可以将类声明为static,这表示它只包含静态成员。无法使用new关键字创建静态类的实例。当加载包含该类的程序或命名空间时,.NET Framework公共语言运行库(CLR)会自动加载静态类。

使用静态类来包含与特定对象无关的方法。例如,创建一组不对实例数据起作用且与代码中的特定对象无关的方法是一种常见的要求。您可以使用静态类来保存这些方法。

以下是静态类的主要功能:

  1. 它们只包含静态成员。

  2. 无法实例化它们。

  3. 它们是密封的。

  4. 它们不能包含实例构造函数(C#编程指南)。

  5. 因此,创建静态类与创建仅包含静态成员和私有构造函数的类基本相同。私有构造函数阻止实例化类。

    使用静态类的优点是编译器可以检查以确保不会意外添加实例成员。编译器将保证无法创建此类的实例。

    静态类是密封的,因此无法继承。它们不能从除Object之外的任何类继承。静态类不能包含实例构造函数;但是,它们可以有一个静态构造函数。有关更多信息,请参阅静态构造函数(C#编程指南)。

答案 10 :(得分:0)

我的答案:糟糕的设计选择。 ;-)

这是一个关于语法影响的有趣辩论。我认为,论点的核心是设计决策导致了密封的静态类。专注于显示在顶层的静态类名称的透明性,而不是在子名称后面隐藏(“混淆”)?可以想象一种可以直接访问基础语言或子语言的语言实现,令人困惑。

一个伪示例,假定以某种方式定义了静态继承。

public static class MyStaticBase
{
    SomeType AttributeBase;
}

public static class MyStaticChild : MyStaticBase
{
    SomeType AttributeChild;
}

将导致:

 // ...
 DoSomethingTo(MyStaticBase.AttributeBase);
// ...

可能(会?)影响与

相同的存储空间
// ...
DoSomethingTo(MyStaticChild.AttributeBase);
// ...

非常令人困惑!

但是等等!编译器将如何处理MyStaticBase和MyStaticChild具有在两者中定义的相同签名?如果该子项比我上面的示例覆盖了该子项,则不会更改相同的存储,也许吗?这会导致更多的混乱。

我相信对于有限的静态继承,有很强的信息空间理由。不久就会有更多限制。此伪代码显示值:

public static class MyStaticBase<T>
{
   public static T Payload;
   public static void Load(StorageSpecs);
   public static void Save(StorageSpecs);
   public static SomeType AttributeBase
   public static SomeType MethodBase(){/*...*/};
}

然后您得到:

public static class MyStaticChild : MyStaticBase<MyChildPlayloadType>
{
   public static SomeType AttributeChild;
   public static SomeType SomeChildMethod(){/*...*/};
   // No need to create the PlayLoad, Load(), and Save().
   // You, 'should' be prevented from creating them, more on this in a sec...
} 

用法如下:

// ...
MyStaticChild.Load(FileNamePath);
MyStaticChild.Save(FileNamePath);
doSomeThing(MyStaticChild.Payload.Attribute);
doSomething(MyStaticChild.AttributeBase);
doSomeThing(MyStaticChild.AttributeChild);
// ...

创建静态子代的人无需了解序列化过程,只要他们了解平台或环境的序列化引擎可能存在的任何限制即可。

统计信息(子集和其他形式的“全局变量”)经常出现在配置存储周围。静态继承将使这种职责分配可以在语法中清晰地表示出来,以匹配配置的层次结构。但是,正如我所展示的,如果实现基本的静态继承概念,则存在很大的歧义。

我相信正确的设计选择将是允许具有特定限制的静态继承:

  1. 不得覆盖任何内容。孩子不能更换底座 属性,字段或方法,...重载应该可以,例如 只要签名存在差异,编译器就可以 整理儿童与基础。
  2. 仅允许通用静态基,您不能从 非通用静态基。

您仍然可以通过通用引用MyStaticBase<ChildPayload>.SomeBaseField来更改同一商店。但是,由于必须指定泛型类型,因此您不希望这样做。虽然子引用会更干净:MyStaticChild.SomeBaseField

我不是编译器作家,所以我不确定是否缺少在编译器中实现这些限制的困难。就是说,我坚信信息空间需要有限的静态继承,而基本的答案是您不能因为设计错误(或过于简单)而无法实现。

答案 11 :(得分:-1)

当我们创建一个只包含静态成员和私有构造函数的静态类时。唯一的原因是静态构造函数阻止实例化类,因为我们无法继承静态类。访问该类的唯一方法是使用类名本身的静态类的成员。尝试继承静态类不是一个好主意。