나는 최근에 약간의 공급자 와 함께 일해 왔으며 추상 정적 메소드를 가진 추상 클래스를 갖고 싶었던 흥미로운 상황을 발견했습니다. 주제에 대한 몇 가지 게시물을 읽었으며 이해가 되긴하지만 명확한 설명이 있습니까?
나는 최근에 약간의 공급자 와 함께 일해 왔으며 추상 정적 메소드를 가진 추상 클래스를 갖고 싶었던 흥미로운 상황을 발견했습니다. 주제에 대한 몇 가지 게시물을 읽었으며 이해가 되긴하지만 명확한 설명이 있습니까?
답변:
정적 메소드는 인스턴스화 되지 않으며 객체 참조없이 사용할 수 있습니다.
정적 메소드에 대한 호출은 객체 참조가 아닌 클래스 이름을 통해 수행되며이를 호출하기위한 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에서는 가상 / 추상 정적 메서드를 사용할 수 없습니다.
Car
가상 정적 CreateFromDescription
팩토리 메소드 가있는 유형 이있는 경우 Car
제약이있는 일반 유형 을 승인 한 코드 T
가 호출 T.CreateFromDescription
하여 유형의 자동차를 생성 할 수 T
있습니다. 그러한 메소드를 정의한 각 유형이 가상 "정적"메소드를 보유하는 중첩 클래스 제네릭의 정적 싱글 톤 인스턴스를 보유한 경우 이러한 구성은 CLR 내에서 꽤 잘 지원 될 수 있습니다.
정적 메소드는 상속되거나 재정의 될 수 없으므로 추상적 일 수 없습니다. 정적 메소드는 클래스의 인스턴스가 아닌 유형에 정의되므로 해당 유형에서 명시 적으로 호출해야합니다. 따라서 자식 클래스에서 메소드를 호출하려면 해당 이름을 사용하여 호출해야합니다. 이것은 상속을 무의미하게 만듭니다.
정적 메소드를 잠시 상속받을 수 있다고 가정하십시오. 이 시나리오를 상상해보십시오.
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 ()를 호출하면 어떤 메소드가 호출됩니까? 어떤 값이 반환 되었습니까? 객체 인스턴스를 만들지 않으면 상속이 어렵다는 것을 쉽게 알 수 있습니다. 상속이없는 추상 메소드는 본문이없는 메소드이므로 호출 할 수 없습니다.
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
가 구매 주문이 주어진 인스턴스를 빌드 할 수있는 메소드를 정의해야하는 등의 경우 에도 유용합니다 .
정적 필드와 메소드에 대한 상속이 필요한 상황은 다음과 같습니다.
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"?
legs
정적 추상 속성이어야합니다.
이전 설명에 추가하기 위해 정적 메서드 호출은 컴파일 타임에 특정 메서드에 바인딩되어 다형성 동작을 배제합니다.
우리는 실제로 정적 메소드 (델파이에서)를 무시합니다. 조금 추악하지만 우리의 요구에 잘 맞습니다.
우리는 클래스 인스턴스없이 클래스가 사용 가능한 객체 목록을 가질 수 있도록 사용합니다. 예를 들어 다음과 같은 메소드가 있습니다.
class function AvailableObjects: string; override;
begin
Result := 'Object1, Object2';
end;
추악하지만 필요합니다.이 방법으로 사용 가능한 객체를 검색하기 위해 모든 클래스를 인스턴스화하는 대신 필요한 것을 인스턴스화 할 수 있습니다.
이것은 간단한 예이지만 응용 프로그램 자체는 하나의 서버에서 사용할 수있는 모든 클래스를 가진 클라이언트-서버 응용 프로그램이며 서버에있는 모든 것을 필요로하지 않고 개체 인스턴스가 필요하지 않은 여러 다른 클라이언트입니다.
따라서 각 클라이언트마다 하나의 다른 서버 응용 프로그램을 갖는 것보다 유지 관리가 훨씬 쉽습니다.
예제가 명확하기를 바랍니다.
추상 메소드는 내재적으로 가상입니다. 추상 메소드에는 인스턴스가 필요하지만 정적 메소드에는 인스턴스가 없습니다. 따라서 추상 클래스에서 정적 메소드를 가질 수 있으며 정적 추상 (또는 추상 정적) 일 수는 없습니다.