我有几个类实际上不需要任何状态。从组织的角度来看,我想把他们放在层次结构中。
但是我似乎不能为静态类声明继承。
就像这样:
public static class Base
{
}
public static class Inherited : Base
{
}
不会起作用。
为什么语言的设计者们拒绝了这种可能性呢?
我有几个类实际上不需要任何状态。从组织的角度来看,我想把他们放在层次结构中。
但是我似乎不能为静态类声明继承。
就像这样:
public static class Base
{
}
public static class Inherited : Base
{
}
不会起作用。
为什么语言的设计者们拒绝了这种可能性呢?
引用本文:
This is actually by design. There seems to be no good reason to inherit a static class. It has public static members that you can always access via the class name itself. The only reasons I have seen for inheriting static stuff have been bad ones, such as saving a couple of characters of typing. There may be reason to consider mechanisms to bring static members directly into scope (and we will in fact consider this after the Orcas product cycle), but static class inheritance is not the way to go: It is the wrong mechanism to use, and works only for static members that happen to reside in a static class. (Mads Torgersen, C# Language PM)
来自9频道的其他观点
Inheritance in .NET works only on instance base. Static methods are defined on the type level not on the instance level. That is why overriding doesn't work with static methods/properties/events... Static methods are only held once in memory. There is no virtual table etc. that is created for them. If you invoke an instance method in .NET, you always give it the current instance. This is hidden by the .NET runtime, but it happens. Each instance method has as first argument a pointer (reference) to the object that the method is run on. This doesn't happen with static methods (as they are defined on type level). How should the compiler decide to select the method to invoke? (littleguru)
作为一个有价值的想法,littleuru对这个问题有一个部分的“变通方案”:单例模式。
可以这样考虑:你通过类型名访问静态成员,就像这样:
MyStaticType.MyStaticMember();
如果你要从这个类继承,你必须通过新的类型名来访问它:
MyNewType.MyStaticMember();
因此,在代码中使用时,新项与原始项没有关系。没有办法利用任何继承关系来处理多态性之类的事情。
也许您只是想扩展原始类中的一些项。在这种情况下,没有什么可以阻止您在一个全新的类型中使用原始类型的成员。
也许您希望向现有静态类型添加方法。您已经可以通过扩展方法做到这一点。
也许您希望能够在运行时将静态类型传递给函数,并调用该类型的方法,而不需要确切地知道该方法的功能。在这种情况下,您可以使用接口。
所以,最终你不会从继承静态类中获得任何东西。
不能继承静态类的主要原因是它们是抽象且密封的(这也阻止了创建它们的任何实例)。
所以这个:
static class Foo { }
编译到这个IL:
.class private abstract auto ansi sealed beforefieldinit Foo
extends [mscorlib]System.Object
{
}
您希望通过使用类层次结构实现的目标可以通过命名空间实现。因此,支持名称空间的语言(如c#)将无法实现静态类的类层次结构。因为您不能实例化任何类,所以您所需要的只是通过使用名称空间获得的类定义的分层组织
虽然可以通过继承的类名访问“继承的”静态成员,但静态成员并不是真正继承的。这就是为什么它们不能是虚拟的或抽象的,也不能被覆盖的部分原因。在你的例子中,如果你声明了一个Base.Method(),编译器无论如何都会将对Inherited.Method()的调用映射回Base.Method()。您也可以显式地调用Base.Method()。您可以编写一个小测试,并使用Reflector查看结果。
所以…如果不能继承静态成员,并且静态类只能包含静态成员,那么继承静态类又有什么用呢?
静态类和类成员用于创建可以在不创建类实例的情况下访问的数据和函数。静态类成员可用于分离独立于任何对象标识的数据和行为:无论对象发生了什么,数据和函数都不会改变。当类中没有依赖于对象标识的数据或行为时,可以使用静态类。
类可以声明为静态的,这表明它只包含静态成员。不能使用new关键字创建静态类的实例。当包含静态类的程序或命名空间被加载时,由. net Framework公共语言运行库(CLR)自动加载静态类。
使用静态类来包含与特定对象不关联的方法。例如,通常需要创建一组不作用于实例数据且与代码中的特定对象不关联的方法。您可以使用静态类来保存这些方法。
以下是静态类的主要特性:
它们只包含静态成员。 它们不能被实例化。 它们是密封的。 它们不能包含实例构造函数(c#编程指南)。
因此,创建静态类基本上与创建只包含静态成员和私有构造函数的类相同。私有构造函数阻止类被实例化。
使用静态类的好处是编译器可以检查以确保没有意外添加实例成员。编译器将保证不能创建该类的实例。
静态类是密封的,因此不能被继承。它们不能从Object以外的任何类继承。静态类不能包含实例构造函数;但是,它们可以有一个静态构造函数。要了解更多信息,请参阅静态构造函数(c#编程指南)。
当创建只包含静态成员和私有构造函数的静态类时。唯一的原因是静态构造函数阻止类被实例化,因为我们不能继承静态类。访问静态类成员的唯一方法是使用类名本身。试图继承静态类并不是一个好主意。
你可以做一些看起来像静态继承的事情。
诀窍是这样的:
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关键字。但是这种方法有助于支持一些与静态继承非常相似的东西。
注: 我们使用它来创建已编译查询,这实际上可以用ConcurrentDictionary代替,但是具有线程安全性的静态只读字段就足够了。
你可以做的一个解决方法是不使用静态类,而是隐藏构造函数,这样类的静态成员是类外部唯一可访问的东西。结果是一个可继承的“静态”类:
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>();
不幸的是,由于“by-design”语言限制,我们无法做到:
public static class TestClass<T>
{
public static T Add(T x, T y)
{
return (dynamic)x + (dynamic)y;
}
}
public static class TestClass : TestClass<double>
{
}
我的回答是:糟糕的设计选择。: -)
这是一场关于语法影响的有趣辩论。在我看来,争论的核心是一个设计决策导致了密封的静态类。关注静态类的名称出现在顶层的透明度,而不是隐藏在子名称后面(“混乱”)?人们可以想象语言实现可以直接访问基类或子类,这很令人困惑。
这是一个伪示例,假设以某种方式定义了静态继承。
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);
// ...
创建静态子对象的人不需要考虑序列化过程,只要他们了解平台或环境的序列化引擎上可能存在的任何限制。
静态变量(单例和其他形式的“全局变量”)经常出现在配置存储中。静态继承将允许这种职责分配在语法中清晰地表示,以匹配配置的层次结构。但是,正如我所指出的,如果实现基本的静态继承概念,就有可能产生大量的歧义。
我相信正确的设计选择是允许静态继承,但有特定的限制:
没有重写任何东西。子元素不能替换基底元素 属性、字段或方法…重载应该是可以的 只要有不同的签名允许编译器 整理出child和base。 只允许通用静态基,不能继承 非通用静态基。
您仍然可以通过通用引用MyStaticBase<ChildPayload>. somebasefield来更改相同的存储。但是您可能会感到气馁,因为必须指定泛型类型。而子引用会更干净:MyStaticChild.SomeBaseField。
我不是编译器的作者,所以我不确定我是否忽略了在编译器中实现这些限制的困难。也就是说,我坚信有限的静态继承需要信息空间,基本的答案是你不能因为糟糕的(或过于简单的)设计选择。
当我试图对第三方库编写IComparer<T>实现时,我遇到了这个问题,其中T是嵌入在类中的enum,如下所示:
public class TheClass
{
public enum EnumOfInterest
{
}
}
但是因为枚举是在第三方库类中定义的,所以我不能编写比较器,因为下面给出了“不能扩展列表”错误:
public class MyComparer : IComparer<TheClass.EnumOfInterest>
{
}
我甚至没有扩展静态类——我只是实现类中定义的枚举的比较器。