C #에서 추상 정적 메서드를 사용할 수없는 이유는 무엇입니까?


182

나는 최근에 약간의 공급자 와 함께 일해 왔으며 추상 정적 메소드를 가진 추상 클래스를 갖고 싶었던 흥미로운 상황을 발견했습니다. 주제에 대한 몇 가지 게시물을 읽었으며 이해가 되긴하지만 명확한 설명이 있습니까?


3
나중에 개선 할 수 있도록 열어 두십시오.
Mark Biek

6
나는 C #이 이런 종류의 상황을 위해 다른 키워드가 필요하다는 사실에 의문을 제기하고 있다고 생각합니다. 리턴 값이 호출 된 유형에만 의존하는 메소드를 원합니다. 해당 유형을 알 수 없으면 "정적"이라고 부를 수 없습니다. 그러나 유형이 알려지면 정적이됩니다. "정적되지 않은 정적"은 아직 정적 인 것이 아니라 일단 수신 유형을 알면 그대로입니다. 이것은 완벽하게 좋은 개념이므로 프로그래머가 계속 요구합니다. 그러나 디자이너들이 언어에 대해 생각하는 방식에는 맞지 않았습니다.
William Jockusch

@WilliamJockusch 수신 유형은 무엇을 의미합니까? BaseClass.StaticMethod ()를 호출하면 BaseClass가 의사 결정에 사용할 수있는 유일한 유형입니다. 그러나이 수준에서는 추상적이므로 방법을 해결할 수 없습니다. 대신 DerivedClass.StaticMethod를 잘 호출하면 기본 클래스는 관련이 없습니다.
Martin Capodici

기본 클래스에서는 메서드가 확인되지 않아 사용할 수 없습니다. 파생 된 유형 또는 객체 (유도 된 유형을 가짐)가 필요합니다. baseClassObject.Method () 또는 DerivedClass.Method ()를 호출 할 수 있어야합니다. BaseClass.Method ()는 타입을 제공하지 않기 때문에 호출 할 수 없습니다.
William Jockusch

답변:


157

정적 메소드는 인스턴스화 되지 않으며 객체 참조없이 사용할 수 있습니다.

정적 메소드에 대한 호출은 객체 참조가 아닌 클래스 이름을 통해 수행되며이를 호출하기위한 IL (Intermediate Language) 코드는이를 정의한 클래스의 이름을 통해 추상 메소드를 호출합니다. 사용한 수업.

예를 보여 드리겠습니다.

다음 코드로

public class A
{
    public static void Test()
    {
    }
}

public class B : A
{
}

B.Test를 호출하면 다음과 같이됩니다.

class Program
{
    static void Main(string[] args)
    {
        B.Test();
    }
}

Main 메서드 내부의 실제 코드는 다음과 같습니다.

.entrypoint
.maxstack 8
L0000: nop 
L0001: call void ConsoleApplication1.A::Test()
L0006: nop 
L0007: ret 

보시다시피, A.Test는 B.Test가 아닌 A 클래스이므로 코드를 작성할 수는 있지만 A.Test를 호출합니다.

델파이 에서처럼 객체가 아닌 유형을 참조하는 변수를 만들 수있는 클래스 유형 이 있다면 가상 및 추상 정적 메소드 (및 생성자)에 더 많이 사용되지만 사용할 수는 없으며 따라서 정적 호출은 .NET에서 비 가상적입니다.

IL 디자이너가 코드를 컴파일하여 B.Test를 호출하고 런타임에 호출을 해결할 수 있음을 알고 있지만 여전히 클래스 이름을 작성해야하므로 가상적이지는 않습니다.

가상 메소드와 추상 메소드는 런타임에 다양한 유형의 오브젝트를 포함 할 수있는 변수를 사용하는 경우에만 유용하므로 변수에있는 현재 오브젝트에 대해 올바른 메소드를 호출하려고합니다. 정적 메소드를 사용하면 어쨌든 클래스 이름을 거쳐야하므로 호출 할 정확한 메소드는 컴파일 타임에 알 수 없으며 변경되지 않기 때문에 알 수 있습니다.

따라서 .NET에서는 가상 / 추상 정적 메서드를 사용할 수 없습니다.


4
C #에서 연산자 오버로드가 수행되는 방식과 결합하면 불행히도 주어진 연산자 오버로드에 대한 구현을 제공하기 위해 서브 클래스가 필요하지 않습니다.
Chris Moschini

23
이 답변 은 추상적이며 잠재적으로 . \에 정의되어 Test()있는 A것이 아니라 B. \

5
제네릭 형식 매개 변수는 지속 불가능한 "유형"변수로 효과적으로 작동하며 이러한 상황에서 가상 정적 메서드가 유용 할 수 있습니다. 예를 들어, Car가상 정적 CreateFromDescription팩토리 메소드 가있는 유형 이있는 경우 Car제약이있는 일반 유형 을 승인 한 코드 T가 호출 T.CreateFromDescription하여 유형의 자동차를 생성 할 수 T있습니다. 그러한 메소드를 정의한 각 유형이 가상 "정적"메소드를 보유하는 중첩 클래스 제네릭의 정적 싱글 톤 인스턴스를 보유한 경우 이러한 구성은 CLR 내에서 꽤 잘 지원 될 수 있습니다.
supercat 2016 년

45

정적 메소드는 상속되거나 재정의 될 수 없으므로 추상적 일 수 없습니다. 정적 메소드는 클래스의 인스턴스가 아닌 유형에 정의되므로 해당 유형에서 명시 적으로 호출해야합니다. 따라서 자식 클래스에서 메소드를 호출하려면 해당 이름을 사용하여 호출해야합니다. 이것은 상속을 무의미하게 만듭니다.

정적 메소드를 잠시 상속받을 수 있다고 가정하십시오. 이 시나리오를 상상해보십시오.

public static class Base
{
    public static virtual int GetNumber() { return 5; }
}

public static class Child1 : Base
{
    public static override int GetNumber() { return 1; }
}

public static class Child2 : Base
{
    public static override int GetNumber() { return 2; }
}

Base.GetNumber ()를 호출하면 어떤 메소드가 호출됩니까? 어떤 값이 반환 되었습니까? 객체 인스턴스를 만들지 않으면 상속이 어렵다는 것을 쉽게 알 수 있습니다. 상속이없는 추상 메소드는 본문이없는 메소드이므로 호출 할 수 없습니다.


33
귀하의 시나리오를 감안할 때 Base.GetNumber ()는 5를 반환한다고 말합니다. Child1.GetNumber ()는 1을 반환합니다. Child2.GetNumber ()는 2를 반환합니다. 당신의 추론을 이해하도록 돕기 위해 당신이 나를 잘못 증명할 수 있습니까? 감사합니다
Luis Filipe

Base.GetNumber ()가 5를 반환한다고 생각하는면은 이미 진행중인 작업을 이해했음을 의미합니다. 기본 값을 반환하면 상속이 수행되지 않습니다.
David Wengier

60
왜 세상에서 Base.GetNumber ()가 5 이외의 다른 것을 반환합니까? 기본 클래스의 메소드입니다. 옵션은 하나뿐입니다.
Artem Russakovskii

4
@ArtemRussakovskii : 하나가 있다고 가정하자 int DoSomething<T>() where T:Base {return T.GetNumber();}. DoSomething<Base>()5를 DoSomething<Child2>()반환하면 2를 반환 하면 유용 할 것 입니다. 이러한 능력은 장난감 예제뿐만 아니라에서 class Car {public static virtual Car Build(PurchaseOrder PO);}파생 된 모든 클래스 Car가 구매 주문이 주어진 인스턴스를 빌드 할 수있는 메소드를 정의해야하는 등의 경우 에도 유용합니다 .
supercat

4
비 정적 상속과 동일한 "문제"가 있습니다.
Ark-kun

18

또 다른 응답자 (McDowell)는 다형성이 객체 인스턴스에서만 작동한다고 말했다. 그것은 자격이 있어야합니다. 클래스를 "클래스"또는 "메타 클래스"유형의 인스턴스로 취급하는 언어가 있습니다. 이러한 언어는 인스턴스 및 클래스 (정적) 메서드 모두에 다형성을 지원합니다.

C #은 이전의 Java 및 C ++과 같은 언어가 아닙니다. static키워드있어서 정적 바인딩보다는 동적 / 가상임을 표시하기 위해 명시 적으로 사용된다.


9

정적 필드와 메소드에 대한 상속이 필요한 상황은 다음과 같습니다.

abstract class Animal
{
  protected static string[] legs;

  static Animal() {
    legs=new string[0];
  }

  public static void printLegs()
  {
    foreach (string leg in legs) {
      print(leg);
    }
  }
}


class Human: Animal
{
  static Human() {
    legs=new string[] {"left leg", "right leg"};
  }
}


class Dog: Animal
{
  static Dog() {
    legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"};
  }
}


public static void main() {
  Dog.printLegs();
  Human.printLegs();
}


//what is the output?
//does each subclass get its own copy of the array "legs"?

4
아니요, 'legs'배열의 인스턴스는 하나뿐입니다. 정적 생성자가 어떤 순서로 호출 될지 모르기 때문에 출력은 결정적이지 않습니다 (실제로 기본 클래스 정적 생성자가 전혀 호출되지는 않습니다). '필요'는 '욕망'이 더 정확할 수있는 상당히 절대적인 용어입니다.
Sam

legs정적 추상 속성이어야합니다.
리틀 엔디안

8

이전 설명에 추가하기 위해 정적 메서드 호출은 컴파일 타임에 특정 메서드에 바인딩되어 다형성 동작을 배제합니다.


C #은 정적으로 입력됩니다. 다형성 메소드에 대한 호출도 컴파일 타임에 이해합니다. 즉, CLR이 런타임 중에 호출 할 메소드를 해결하기 위해 남아 있지 않습니다.
Adam Tolley

그렇다면 다형성이 CLR에서 정확히 어떻게 작동한다고 생각하십니까? 귀하의 설명은 가상 메소드 디스패치를 ​​배제했습니다.
Rytmis

그것은 실제로 유용한 코멘트가 아닙니다. 나는 유용한 담론을 초청했다. ( '내가 이해 한대로') 사람들이 좀 더 많은 내용을 제공 할 수 있다고 생각한다. 비록 똑같은 죄책감이있는 것 같습니다-나는 위의 의견을 질문으로 의미했습니다 .C #은 컴파일시에 이러한 것들을 평가하지 않습니까?
Adam Tolley

사과, 나는 모욕을 의미하지는 않았다 (그러나 나는 약간의 반응을 인정하지만 ;-). 내 질문의 요점은 다음과 같은 클래스가 있다면 : Class Base {public virtual void Method (); } class 파생 : 기본 {public override void Method (); } 다음과 같이 작성하십시오. 기본 인스턴스 = new Derived (); instance.Method (); 호출 사이트의 컴파일 타임 유형 정보는 실제 인스턴스가 Derived 일 때 Base 인스턴스가 있다는 것입니다. 따라서 컴파일러는 호출 할 정확한 메서드를 확인할 수 없습니다. 대신 런타임에 디스패치하도록 알려주는 "callvirt"IL 명령을 내
보냅니다

1
고마워, 그게 유익한! 내가 일리노이에 다이빙을 충분히 오랫동안 해왔 던 것 같아요, 행운을 빌어 요.
Adam Tolley 2016 년

5

우리는 실제로 정적 메소드 (델파이에서)를 무시합니다. 조금 추악하지만 우리의 요구에 잘 맞습니다.

우리는 클래스 인스턴스없이 클래스가 사용 가능한 객체 목록을 가질 수 있도록 사용합니다. 예를 들어 다음과 같은 메소드가 있습니다.

class function AvailableObjects: string; override;
begin
  Result := 'Object1, Object2';
end; 

추악하지만 필요합니다.이 방법으로 사용 가능한 객체를 검색하기 위해 모든 클래스를 인스턴스화하는 대신 필요한 것을 인스턴스화 할 수 있습니다.

이것은 간단한 예이지만 응용 프로그램 자체는 하나의 서버에서 사용할 수있는 모든 클래스를 가진 클라이언트-서버 응용 프로그램이며 서버에있는 모든 것을 필요로하지 않고 개체 인스턴스가 필요하지 않은 여러 다른 클라이언트입니다.

따라서 각 클라이언트마다 하나의 다른 서버 응용 프로그램을 갖는 것보다 유지 관리가 훨씬 쉽습니다.

예제가 명확하기를 바랍니다.


0

추상 메소드는 내재적으로 가상입니다. 추상 메소드에는 인스턴스가 필요하지만 정적 메소드에는 인스턴스가 없습니다. 따라서 추상 클래스에서 정적 메소드를 가질 수 있으며 정적 추상 (또는 추상 정적) 일 수는 없습니다.


1
-1 가상 메서드는 디자인을 제외하고 인스턴스가 필요하지 않습니다. 그리고 당신은 실제로 그 문제를 해결하지는 않습니다.
FallenAvatar
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.