web-dev-qa-db-ja.com

.NET:静的メソッドで「this」クラスのタイプを決定する

非静的メソッドでは、this.GetType()を使用でき、Typeを返します。静的メソッドで同じTypeを取得するにはどうすればよいですか?もちろん、ThisTypeNameはランタイムでのみ知られているため、typeof(ThisTypeName)を書くことはできません。ありがとう!

88
Yegor

静的メソッドのthis.GetType()と同等の1ライナーを探している場合は、次を試してください。

_Type t = MethodBase.GetCurrentMethod().DeclaringType
_

これはおそらくtypeof(TheTypeName)を使用するよりもはるかに高価です。

126
JaredPar

他の回答ではまだ明確にされていないものがあります。これは、実行時にのみ使用可能な型という考えに関連しています。

派生型を使用して静的メンバーを実行する場合、real型名はバイナリで省略されます。たとえば、次のコードをコンパイルします。

UnicodeEncoding.GetEncoding(0);

ここでildasmを使用します...呼び出しが次のように発行されることがわかります。

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

コンパイラはEncoding.GetEncodingの呼び出しを解決しました-UnicodeEncodingの痕跡は残っていません。それはあなたの「現在のタイプ」の考えを無意味にします、私は恐れています。

59
Jon Skeet

別の解決策は、自己参照型を使用することです

//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();

-ロブ

24
Rob Leclerc

静的メソッドでは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());
  }
}
6
John Feminella

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>);
    }
}

ここで明らかな何かを見逃していますか?

3
Tarydon

[〜#〜] edit [〜#〜]このメソッドは、 markmnl 私に指摘した。

そうでなければ、検出されるべき大きな問題になります。開発ではうまく機能しますが、本番ではうまくいかないかもしれません。


ユーティリティメソッドは、コードのあらゆる場所から必要なときにメソッドを呼び出すだけです。

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;
}
0
T-moty

私の目的では、@ 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
Kabuo

メンバーが静的である場合、実行時にどのタイプのメンバーであるかが常にわかります。この場合:

class A
{
  public static int GetInt(){}

}
class B : A {}

電話をかけることはできません(編集:どうやら、以下のコメントを見ることができますが、Aに電話をかけることになります)。

B.GetInt();

メンバーは静的であるため、継承シナリオには関与しません。エルゴ、あなたはいつもタイプがAであることを知っています。

0
Teun D