.NET : 정적 메서드에서 "this"클래스의 유형 확인


94

비 정적 메서드에서 사용할 수 this.GetType()있으며 Type. Type정적 메서드에서 어떻게 똑같이 얻을 수 있습니까? 물론 런타임에서만 알려져 typeof(ThisTypeName)있기 때문에 그냥 쓸 수는 없습니다 ThisTypeName. 감사!


16
STATIC 컨텍스트에 있으며 typeof (ThisTypeName)를 쓸 수 없습니까? 어떻게?
Bruno Reis

1
정적 메서드 내부에는 '런타임'과 같은 것이 없습니다 (정적 메서드에 전달되는 인수에 대해 이야기하지 않는다고 가정). 이 경우 단순히 typeof (RelevantType)이라고 말할 수 있습니다.
Manish Basantani

2
정적 메서드는 가상이 될 수 없습니다. 당신은 이미 유형을 알고 있습니다.
Hans Passant

7
추상 클래스에서 파생 된 클래스가 많이 있습니다. 기본 추상 클래스에는 정적 dictionary <Int, Type>이 있습니다. 따라서 파생 클래스는 정적 생성자 (dic.Add (N, T))에 자신을 "등록"합니다. 그리고 예, 저는 유형을 압니다. :) 저는 약간 게으르고 텍스트를 바꾸는 것을 좋아하지 않으며 "T"가 런타임에 결정될 수 있는지 궁금합니다. 질문을 단순화하는 데 필요했기 때문에 내 거짓말을 용서하십시오. 그리고 그것은 작동했습니다;) 이제 받아 들여진 해결책이 있습니다. 감사.
Yegor

서브 클래스는 수퍼 클래스의 정적 메서드를 상속합니다. 슈퍼 클래스 정적 메서드가 모든 하위 클래스에 유용하다는 것이 이치에 맞지 않습니까? 정적은 단순히 인스턴스가 없다는 것을 의미합니다. 공통 기본 클래스의 공통 코드 원칙이 인스턴스 메서드뿐만 아니라 정적 메서드에도 적용됩니까?
Matt Connolly

답변:


134

this.GetType()정적 메서드에 해당하는 1 라이너를 찾고 있다면 다음을 시도하십시오.

Type t = MethodBase.GetCurrentMethod().DeclaringType

.NET을 사용하는 것보다 훨씬 비쌉니다 typeof(TheTypeName).


1
이것은 잘 작동합니다. 고마워요 :) 아주 드물다고 할 수 있기 때문에 그렇게 비싸지 않습니다.
예고 르

2
Entrase, "비싼"Jared는 프로세서 비용이 많이 들고 일반적으로 느림을 의미합니다. 그러나 그는 "훨씬 더 비싸다"는 것은 더 느리다는 것을 의미한다고 말했다. 로켓 유도 시스템을 설계하지 않는 한 전혀 느리지 않을 것입니다.
Dan Rosenstark

1
GetCurrentMethod가 심각한 성능 문제를 일으키는 것을 보았습니다. 그러나 유형을 가져 오기 때문에 캐시 할 수 있습니다.
Jonathan Allen

51
이것은 항상 서브 클래스의 경우에 호출 된 클래스가 아니라 현재 메서드를 구현하는 클래스를 반환합니다.
Matt Connolly

3
코드가 다른 클래스 이름이나 다른 이름으로 마이그레이션되는 경우 오류를 피하는 것이 편리하지만 typeof(TheTypeName)어쨌든 좋은 리팩토링 도구가 처리해야 합니다.
Nyerguds

59

다른 답변이 명확하지 않은 것이 있으며 실행 시간에만 사용할 수있는 유형에 대한 아이디어와 관련이 있습니다.

파생 형식을 사용하여 정적 멤버를 실행하는 경우 이진에서 실제 형식 이름이 생략됩니다. 예를 들어 다음 코드를 컴파일합니다.

UnicodeEncoding.GetEncoding(0);

이제 ildasm을 사용하십시오 ... 호출이 다음과 같이 방출되는 것을 볼 수 있습니다.

IL_0002:  call       class [mscorlib]System.Text.Encoding 
[mscorlib]System.Text.Encoding::GetEncoding(int32)

컴파일러가에 대한 호출을 해결했습니다 . 남은 Encoding.GetEncoding흔적이 없습니다 UnicodeEncoding. 그것은 "현재 유형"에 대한 당신의 생각을 무의미하게 만듭니다.


10 년을 앞두고 C #에 여전히 가상 정적이없는 이유는 무엇입니까? ;) 보통은 그것들이 필요하지 않을 것입니다 ... 그러나 그들이 유용 할 때 드문 경우가 있습니다;)
marchewek

@marchewek : 아주 드물다고 말하고 싶습니다. C # 팀이 항상 더 유용한 일을 찾아 낼만큼 드물습니다. (그들이이 내내 유휴 상태가 아니었다 ...)
Jon Skeet

24

또 다른 해결책은 자기 참조 유형을 사용하는 것입니다.

//My base class
//I add a type to my base class use that in the static method to check the type of the caller.
public class Parent<TSelfReferenceType>
{
    public static Type GetType()
    {
        return typeof(TSelfReferenceType);
    }
}

그런 다음 상속하는 클래스에서 자체 참조 유형을 만듭니다.

public class Child: Parent<Child>
{
}

이제 Parent 내부의 호출 유형 typeof (TSelfReferenceType)는 인스턴스 없이도 호출자의 유형을 가져와 반환합니다.

Child.GetType();

-롭


저는 이것을 싱글 톤 패턴에 사용했습니다. 즉, Singleton <T> ... 정적 멤버는 오류 메시지 나 필요한 다른 곳에서 typeof (T)를 참조 할 수 있습니다.
yoyo

1
안녕하세요. 정적 기본 함수에서 자식 유형을 찾는 해결 방법을 제공하기 때문에이 답변을 정말 좋아하고 감사합니다.
Bill Software Engineer

1
멋지네요. 하지만 C #에서는이 작은 코드 복제 없이는이를 수행 할 수 없다는 것이 너무 슬프다.
표시 이름

에이 해결 방법의 또 다른 예입니다 stackoverflow.com/a/22532416/448568
스티븐 드 살라스

둘 다 (이것과 Steven에 의해 연결된 것)은 기본 클래스의 구현 자의 상속자에 대해 작동하지 않을 것입니다 ... 손자는 자식 유형으로 끝날 것입니다 ... 너무 나쁜 C #에는 가상 정적이 없습니다;)
marchewek

5

this정적 메서드에서는 사용할 수 없으므로 직접 가능하지 않습니다. 그러나 어떤 객체의 유형이 필요한 경우 해당 객체를 호출 GetType하고 this인스턴스를 전달해야하는 매개 변수로 만듭니다. 예 :

public class Car {
  public static void Drive(Car c) {
    Console.WriteLine("Driving a {0}", c.GetType());
  }
}

그러나 이것은 형편없는 디자인처럼 보입니다. 자체 정적 메서드 내에서 인스턴스 자체의 유형을 실제로 가져와야합니까? 좀 이상해 보입니다. 인스턴스 메서드를 사용하지 않는 이유는 무엇입니까?

public class Car {
  public void Drive() { // Remove parameter; doesn't need to be static.
    Console.WriteLine("Driving a {0}", this.GetType());
  }
}

3

typeof (ThisTypeName)을 사용할 수없는 이유를 이해할 수 없습니다. 이것이 제네릭이 아닌 유형이면 작동합니다.

class Foo {
   static void Method1 () {
      Type t = typeof (Foo); // Can just hard code this
   }
}

제네릭 유형 인 경우 :

class Foo<T> {
    static void Method1 () {
       Type t = typeof (Foo<T>);
    }
}

여기서 명백한 것이 누락 되었습니까?


7
Foo에서 파생 된 Bar 클래스를 생성 한 다음 해당 클래스가 Method1을 상속하면 작동하지 않습니다. 그러면 Bar.Method1에 대한 호출은 여전히 ​​잘못된 typeof (Foo)를 처리합니다. 상속 된 Method1은 Bar에서 파생 된 것을 어떻게 든 알고 있어야하며 typeof (Bar)를 가져옵니다.
JustAMartin 2013 년

0

멤버가 정적 인 경우 런타임에 어떤 유형의 일부인지 항상 알 수 있습니다. 이 경우 :

class A
{
  public static int GetInt(){}

}
class B : A {}

전화를 걸 수 없습니다 (편집 : 분명히 아래 설명을 볼 수 있지만 여전히 A로 전화를 걸 것입니다) :

B.GetInt();

멤버가 정적이기 때문에 상속 시나리오에서 역할을하지 않습니다. Ergo, 당신은 항상 유형이 A라는 것을 알고 있습니다.


4
B.GetInt ()를 호출 있습니다 . 최소한 private가 아니라면 가능합니다. 그러나 컴파일은이를 A.GetInt () 호출로 변환합니다. 시도 해봐!
Jon Skeet

Jon의 의견에 대한 참고 : C #의 기본 가시성은 비공개이므로 예제가 작동하지 않습니다.
Dan Rosenstark

0

제 목적 상 저는 @ T-moty의 아이디어를 좋아합니다. 몇 년 동안 "자기 참조 유형"정보를 사용해 왔지만 나중에 기본 클래스를 참조하는 것이 더 어렵습니다.

예 (위의 @Rob Leclerc 예제 사용) :

public class ChildA: Parent<ChildA>
{
}

public class ChildB: Parent<ChildB>
{
}

예를 들어이 패턴으로 작업하는 것은 어려울 수 있습니다. 함수 호출에서 기본 클래스를 어떻게 반환합니까?

public Parent<???> GetParent() {}

아니면 타입 캐스팅?

var c = (Parent<???>) GetSomeParent();

그래서 나는 할 수있을 때 피하려고 노력하고 필요할 때 사용합니다. 필요한 경우 다음 패턴을 따르는 것이 좋습니다.

class BaseClass
{
    // All non-derived class methods goes here...

    // For example:
    public int Id { get; private set; }
    public string Name { get; private set; }
    public void Run() {}
}

class BaseClass<TSelfReferenceType> : BaseClass
{
    // All derived class methods goes here...

    // For example:
    public TSelfReferenceType Foo() {}
    public void Bar(TSelfRefenceType obj) {}
}

이제 (더) 쉽게 BaseClass. 그러나 현재 상황과 같이 기본 클래스 내에서 파생 클래스를 노출 할 필요가 없으며 @ M-moty의 제안을 사용하는 것이 올바른 접근 방식 일 수 있습니다.

그러나 @ M-moty의 코드를 사용하는 것은 기본 클래스가 호출 스택에 인스턴스 생성자를 포함하지 않는 한만 작동합니다. 불행히도 내 기본 클래스는 인스턴스 생성자를 사용합니다.

따라서 기본 클래스 '인스턴스'생성자를 고려한 확장 메서드는 다음과 같습니다.

public static class TypeExtensions
{
    public static Type GetDrivedType(this Type type, int maxSearchDepth = 10)
    {
        if (maxSearchDepth < 0)
            throw new ArgumentOutOfRangeException(nameof(maxSearchDepth), "Must be greater than 0.");

        const int skipFrames = 2;  // Skip the call to self, skip the call to the static Ctor.
        var stack = new StackTrace();
        var maxCount = Math.Min(maxSearchDepth + skipFrames + 1, stack.FrameCount);
        var frame = skipFrames;

        // Skip all the base class 'instance' ctor calls. 
        //
        while (frame < maxCount)
        {
            var method = stack.GetFrame(frame).GetMethod();
            var declaringType = method.DeclaringType;

            if (type.IsAssignableFrom(declaringType))
                return declaringType;

            frame++;
        }

        return null;
    }
}

0

편집 이 방법은 markmnl 이 나에게 지적했듯이 실행 파일 / 라이브러리와 함께 PDB 파일을 배포 할 때만 작동합니다 .

그렇지 않으면 감지해야 할 큰 문제가 될 것입니다. 개발에서는 잘 작동하지만 프로덕션에서는 작동하지 않을 수 있습니다.


유틸리티 메서드는 코드의 모든 위치에서 필요할 때 간단히 메서드를 호출합니다.

public static Type GetType()
{
    var stack = new System.Diagnostics.StackTrace();

    if (stack.FrameCount < 2)
        return null;

    return (stack.GetFrame(1).GetMethod() as System.Reflection.MethodInfo).DeclaringType;
}

1
StackTrace는 디버그 빌드에서만 사용할 수 있습니다
markmnl

올바르지 않음 : 릴리스 모드에서 .pdb 파일도 배포 할 때 StackTrace를 사용할 수 있습니다. stackoverflow.com/questions/2345957/…
T-moty 2011

나는 당신의 요점을 얻었다. 메서드가 PDB 파일이 배포 된 경우에만 작동한다는 것은 허용되지 않습니다. 나는 답을 수정할 것이다
T-moty
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.