답변:
여기 에서 인용 :
이것은 실제로 의도적으로 설계된 것입니다. 정적 클래스를 상속받을 이유가없는 것 같습니다. 클래스 이름 자체를 통해 항상 액세스 할 수있는 공개 정적 멤버가 있습니다. 정적 인 물건을 상속받는 것으로 본 유일한 이유는 입력하는 두 문자를 저장하는 것과 같은 나쁜 이유였습니다.
정적 멤버를 직접 범위로 가져 오는 메커니즘을 고려해야하는 이유가있을 수 있지만 (Orcas 제품주기 이후에이를 실제로 고려할 것임) 정적 클래스 상속은가는 방법이 아닙니다. 사용하는 잘못된 메커니즘이며 작동합니다. 정적 클래스에있는 정적 멤버에만 해당됩니다.
(Mads Torgersen, C # 언어 PM)
channel9의 다른 의견
.NET의 상속은 인스턴스 기반에서만 작동합니다. 정적 메소드는 인스턴스 레벨이 아닌 유형 레벨에서 정의됩니다. 정적 메서드 / 속성 / 이벤트에서는 재정의가 작동하지 않는 이유입니다 ...
정적 메소드는 메모리에서 한 번만 보유됩니다. 작성된 가상 테이블 등이 없습니다.
.NET에서 인스턴스 메소드를 호출하면 항상 현재 인스턴스를 제공합니다. 이것은 .NET 런타임에 숨겨져 있지만 발생합니다. 각 인스턴스 메소드에는 첫 번째 인수로 메소드가 실행되는 오브젝트에 대한 포인터 (참조)가 있습니다. 정적 메서드에서는 발생하지 않습니다 (유형 수준에서 정의되었으므로). 컴파일러는 호출 할 메소드를 어떻게 선택해야합니까?
(littleguru)
그리고 귀중한 아이디어로서 littleguru 는이 문제에 대한 부분적인 "해결책": Singleton 패턴을 가지고 있습니다.
object
되며 모든 사람이이를 알고 있어야합니다. 따라서 정적 클래스 는object
명시 적으로 지정 여부에 관계없이 항상에서 상속됩니다 .
정적 클래스를 상속 할 수없는 주된 이유는 클래스가 추상적이고 봉인되어 있기 때문입니다 (이로 인해 해당 클래스의 인스턴스가 생성되지 않음).
그래서 이거:
static class Foo { }
이 IL로 컴파일합니다.
.class private abstract auto ansi sealed beforefieldinit Foo
extends [mscorlib]System.Object
{
}
이런 식으로 생각하십시오 : 다음과 같이 유형 이름을 통해 정적 멤버에 액세스하십시오.
MyStaticType.MyStaticMember();
해당 클래스에서 상속받은 경우 새 유형 이름을 통해 클래스에 액세스해야합니다.
MyNewType.MyStaticMember();
따라서 새 항목은 코드에서 사용될 때 원본과 관계가 없습니다. 다형성과 같은 것들에 대한 상속 관계를 이용할 수있는 방법은 없습니다.
아마도 당신은 원래 수업에서 일부 항목을 확장하고 싶다고 생각하고 있습니다. 이 경우 원본의 멤버를 완전히 새로운 유형으로 사용하는 것을 막을 수있는 것은 없습니다.
기존 정적 유형에 메소드를 추가하려고 할 수 있습니다. 확장 방법을 통해 이미 그렇게 할 수 있습니다.
아마도 Type
런타임에 함수에 정적을 전달 하고 메서드의 기능을 정확히 몰라도 해당 유형의 메서드를 호출 하려고 할 수 있습니다 . 이 경우 인터페이스를 사용할 수 있습니다.
따라서 결국 정적 클래스 상속을 통해 아무것도 얻지 못합니다.
SomeClass<T> where T:Foo
의 멤버에 액세스 할 수 Foo
및 경우 T
일부 가상 정적 멤버 오버라이드 클래스했다 Foo
, 일반 클래스가 그 재정의를 사용하는 것입니다. 현재 CLR과 호환되는 방식으로 언어가 수행 할 수있는 규칙을 정의하는 것이 가능할 수도 있습니다 (예 : 해당 멤버가있는 클래스는 정적 필드와 함께 해당 인스턴스 멤버가 포함 된 보호 된 비 정적 클래스를 정의해야 함). 해당 유형의 인스턴스를 보유).
클래스 계층 구조를 사용하여 달성하려는 것은 단순히 네임 스페이스를 통해 달성 할 수 있습니다. 따라서 C #과 같은 네임 스페이스를 지원하는 언어는 정적 클래스의 클래스 계층 구조를 구현할 필요가 없습니다. 클래스를 인스턴스화 할 수 없으므로 네임 스페이스를 사용하여 얻을 수있는 클래스 정의의 계층 구조 만 있으면됩니다.
상속 된 클래스 이름을 통해 "상속 된"정적 멤버에 액세스 할 수 있지만 정적 멤버는 실제로 상속되지는 않습니다. 이것이 부분적으로 가상이거나 추상적 일 수없고 재정의 될 수없는 이유입니다. 예제에서 Base.Method ()를 선언 한 경우 컴파일러는 Inherited.Method ()에 대한 호출을 Base.Method ()에 다시 매핑합니다. Base.Method ()를 명시 적으로 호출 할 수도 있습니다. 작은 테스트를 작성하고 리플렉터로 결과를 볼 수 있습니다.
정적 멤버를 상속 할 수없고 정적 클래스에 정적 멤버 만 포함 할 수 있다면 정적 클래스를 상속하면 어떤 이점이 있습니까?
흠 ... 정적 메소드로 채워진 비 정적 클래스가 있다면 훨씬 다를 것입니다 ..?
해결 방법은 정적 클래스를 사용하지 않고 생성자를 숨기므로 클래스 정적 멤버 만 클래스 외부에서 액세스 할 수있는 것입니다. 결과는 상속 가능한 "정적"클래스입니다.
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>();
불행히도 "설계 상"언어 제한으로 인해 다음과 같은 작업을 수행 할 수 없습니다.
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 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 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);
// ...
정적 하위를 작성하는 사람은 플랫폼 또는 환경의 직렬화 엔진에 적용될 수있는 제한 사항을 이해하는 한 직렬화 프로세스에 대해 생각할 필요가 없습니다.
정적 (싱글 톤 및 다른 형태의 '글로벌')은 종종 구성 저장소 주위에 나타납니다. 정적 상속은 이러한 종류의 책임 할당이 구성 계층 구조와 일치하도록 구문으로 명확하게 표현 될 수 있도록합니다. 내가 보여 주었 듯이, 기본 정적 상속 개념이 구현되면 엄청난 모호성이 생길 수 있습니다.
올바른 디자인 선택은 특정 제한을 가진 정적 상속을 허용하는 것이라고 생각합니다.
일반 참조를 통해 여전히 동일한 상점을 변경할 수 있습니다 MyStaticBase<ChildPayload>.SomeBaseField
. 그러나 제네릭 형식을 지정해야하므로 권장하지 않습니다. 자식 참조는 더 깨끗하지만 : MyStaticChild.SomeBaseField
.
나는 컴파일러 작성자가 아니기 때문에 컴파일러에서 이러한 제한을 구현하는 데 어려움이 있는지 잘 모르겠습니다. 즉, 제한된 정적 상속에 대한 정보 공간이 필요하다는 기본적인 믿음은 기본 디자인이 좋지 않은 (또는 단순한) 디자인 선택으로 인해 불가능하다는 것입니다.
정적 클래스와 클래스 멤버는 클래스 인스턴스를 만들지 않고 액세스 할 수있는 데이터와 함수를 만드는 데 사용됩니다. 정적 클래스 멤버를 사용하면 객체 아이덴티티와 무관 한 데이터와 동작을 분리 할 수 있습니다. 데이터와 함수는 객체에 발생하는 내용에 관계없이 변경되지 않습니다. 클래스에 객체 아이덴티티에 의존하는 데이터 나 동작이 없을 때 정적 클래스를 사용할 수 있습니다.
클래스는 static으로 선언 될 수 있으며, 이는 정적 멤버 만 포함 함을 나타냅니다. 새 키워드를 사용하여 정적 클래스의 인스턴스를 작성할 수 없습니다. 정적 클래스는 클래스가 포함 된 프로그램 또는 네임 스페이스가로드 될 때 .NET Framework CLR (공용 언어 런타임)에 의해 자동으로로드됩니다.
정적 클래스를 사용하여 특정 객체와 관련되지 않은 메소드를 포함하십시오. 예를 들어, 인스턴스 데이터에 대해 작동하지 않고 코드의 특정 객체와 관련되지 않은 메소드 세트를 작성하는 것이 일반적 요구 사항입니다. 정적 클래스를 사용하여 해당 메소드를 보유 할 수 있습니다.
정적 클래스의 주요 기능은 다음과 같습니다.
정적 멤버 만 포함합니다.
인스턴스화 할 수 없습니다.
그들은 봉인되어 있습니다.
인스턴스 생성자를 포함 할 수 없습니다 (C # 프로그래밍 가이드).
따라서 정적 클래스를 만드는 것은 기본적으로 정적 멤버와 개인 생성자 만 포함하는 클래스를 만드는 것과 같습니다. 개인 생성자는 클래스가 인스턴스화되지 않도록합니다.
정적 클래스를 사용하면 컴파일러에서 인스턴스 멤버가 실수로 추가되지 않았는지 확인할 수 있다는 장점이 있습니다. 컴파일러는이 클래스의 인스턴스를 만들 수 없음을 보증합니다.
정적 클래스는 봉인되어 상속 될 수 없습니다. Object를 제외한 모든 클래스에서 상속 할 수 없습니다. 정적 클래스는 인스턴스 생성자를 포함 할 수 없습니다. 그러나 정적 생성자를 가질 수 있습니다. 자세한 내용은 정적 생성자 (C # 프로그래밍 안내서)를 참조하십시오.