.NET: Tentukan jenis kelas "ini" dalam metode statisnya

94

Dalam metode non-statis saya dapat menggunakan this.GetType()dan itu akan mengembalikan Type. Bagaimana saya bisa mendapatkan yang sama Typedalam metode statis? Tentu saja, saya tidak bisa hanya menulis typeof(ThisTypeName)karena ThisTypeNamehanya diketahui dalam runtime. Terima kasih!

Yegor
sumber
16
Anda berada dalam konteks STATIC dan tidak dapat menulis typeof (ThisTypeName)? Bagaimana?
Bruno Reis
1
Tidak ada yang seperti 'runtime' di dalam metode statis (dengan asumsi Anda tidak berbicara tentang argumen yang diteruskan ke metode statis). Dalam hal ini, Anda cukup mengatakan typeof (RelevantType).
Manish Basantani
2
Metode statis tidak bisa virtual. Anda sudah tahu tipenya.
Hans Passant
7
Akan ada banyak kelas turunan dari yang abstrak. Kelas abstrak dasar memiliki kamus statis <Int, Type>. Jadi kelas turunan "mendaftar" sendiri dalam konstruktor statis (dic.Add (N, T)). Dan ya, saya tahu jenisnya :) Saya hanya sedikit malas dan tidak suka mengganti teks dan bertanya-tanya apakah “T” dapat ditentukan saat runtime. Mohon maafkan kebohongan saya, karena itu hanya diperlukan untuk menyederhanakan pertanyaan. Dan itu berhasil;) Ada solusi yang diterima sekarang. Terima kasih.
Yegor
Sebuah subclass mewarisi metode statis dari superclass-nya, bukan? Bukankah masuk akal jika metode statis superclass berguna untuk semua sub-kelasnya? Statis berarti tanpa contoh, apakah prinsip kode umum dalam kelas basis umum berlaku untuk metode statis dan juga metode contoh?
Matt Connolly

Jawaban:

134

Jika Anda mencari 1 liner yang setara dengan this.GetType()metode statis, coba yang berikut ini.

Type t = MethodBase.GetCurrentMethod().DeclaringType

Meskipun ini sepertinya jauh lebih mahal daripada hanya menggunakan typeof(TheTypeName).

JaredPar
sumber
1
Yang ini bekerja dengan baik. Terima kasih :) Tidak semahal itu karena akan disebut cukup langka.
Yegor
2
Entrase, dengan "mahal" Jared berarti mereka mahal untuk prosesor, biasanya berarti lambat. Namun dia berkata, "jauh lebih mahal" artinya lebih lambat. Mungkin tidak lambat sama sekali, kecuali Anda merancang sistem pemandu roket.
Dan Rosenstark
1
Saya telah melihat GetCurrentMethod menyebabkan beberapa masalah kinerja yang serius. Tetapi karena Anda baru saja mendapatkan tipe, Anda dapat menyimpannya dalam cache.
Jonathan Allen
51
Ini selalu mengembalikan kelas yang mengimplementasikan metode saat ini, bukan kelas yang dipanggilnya dalam kasus subkelas.
Matt Connolly
3
Saya rasa Ini berguna untuk menghindari kesalahan jika kode pernah dimigrasi ke nama kelas yang berbeda atau sesuatu, tetapi alat refactoring yang baik harus mengurusnya typeof(TheTypeName).
Nyerguds
59

Ada sesuatu yang jawaban lain belum cukup klarifikasi, dan yang relevan dengan ide Anda tentang jenis yang hanya tersedia pada waktu eksekusi.

Jika Anda menggunakan tipe turunan untuk mengeksekusi anggota statis, nama tipe sebenarnya dihilangkan dalam biner. Jadi misalnya, kompilasi kode ini:

UnicodeEncoding.GetEncoding(0);

Sekarang gunakan ildasm di atasnya ... Anda akan melihat bahwa panggilan dibunyikan seperti ini:

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

Kompilator telah menyelesaikan panggilan ke Encoding.GetEncoding- tidak ada jejak UnicodeEncodingkiri. Itu membuat gagasan Anda tentang "tipe saat ini" tidak masuk akal, saya khawatir.

Jon Skeet
sumber
Maju cepat 10 tahun dan mengapa tidak ada statika virtual di C #? ;) biasanya orang tidak membutuhkannya ... tetapi ada kesempatan langka ketika mereka berguna;)
marchewek
@marchewek: Sangat, sangat jarang menurut saya - cukup jarang tim C # selalu menemukan hal yang lebih berguna untuk dilakukan. (Ini tidak seperti mereka menganggur selama ini ...)
Jon Skeet
24

Solusi lain adalah dengan menggunakan jenis referensi sendiri

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

Kemudian di kelas yang mewarisinya, saya membuat tipe referensi mandiri:

public class Child: Parent<Child>
{
}

Sekarang jenis panggilan typeof (TSelfReferenceType) di dalam Parent akan mendapatkan dan mengembalikan Jenis pemanggil tanpa memerlukan instance.

Child.GetType();

-Rampok

Rob Leclerc
sumber
Saya telah menggunakan ini untuk pola tunggal, yaitu Singleton <T> ... anggota statis kemudian dapat merujuk ke typeof (T) dalam pesan kesalahan atau di mana pun itu diperlukan.
yoyo
1
Hai. Saya sangat menyukai dan menghargai jawaban ini karena ini memberi saya solusi untuk menemukan jenis anak dari fungsi dasar statis.
Bill Software Engineer
1
Bagus. Sayangnya, di C # hal ini tidak dapat dilakukan tanpa duplikasi kode kecil ini.
Nama Tampilan
Ada contoh lain dari solusi ini di stackoverflow.com/a/22532416/448568
Steven de Salas
Keduanya (yang ini dan yang ditautkan oleh Steven) tidak akan berfungsi untuk pewaris dari pelaksana kelas dasar ... Cucu akan berakhir dengan tipe Anak ... Sayang sekali C # tidak memiliki statika virtual;)
marchewek
5

Anda tidak dapat menggunakan thismetode statis, jadi itu tidak mungkin dilakukan secara langsung. Namun, jika Anda memerlukan tipe dari beberapa objek, panggil saja objek GetTypetersebut dan jadikan thisinstance sebagai parameter yang harus Anda teruskan, misalnya:

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

Ini sepertinya desain yang buruk. Apakah Anda yakin bahwa Anda benar-benar perlu mendapatkan jenis instance itu sendiri di dalam metode statisnya sendiri? Sepertinya itu agak aneh. Mengapa tidak menggunakan metode contoh saja?

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

Saya tidak mengerti mengapa Anda tidak dapat menggunakan typeof (ThisTypeName). Jika ini adalah tipe non-generik, maka ini akan bekerja:

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

Jika itu adalah tipe generik, maka:

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

Apakah saya melewatkan sesuatu yang jelas di sini?

Tarydon
sumber
7
Ini tidak akan berfungsi jika Anda membuat Bar kelas yang diturunkan dari Foo dan kemudian kelas tersebut mewarisi Metode1 - kemudian panggilan ke Bar.Method1 akan tetap memproses typeof (Foo) yang salah. Metode1 yang diwariskan harus tahu bahwa itu diturunkan di Bar, dan kemudian mendapatkan typeof (Bar).
JustAMartin
0

Ketika anggota Anda statis, Anda akan selalu tahu jenisnya bagian dari saat runtime. Pada kasus ini:

class A
{
  public static int GetInt(){}

}
class B : A {}

Anda tidak dapat memanggil (edit: ternyata, Anda bisa, lihat komentar di bawah, tetapi Anda masih akan memanggil ke A):

B.GetInt();

karena anggotanya statis, ia tidak berperan dalam skenario warisan. Ergo, Anda selalu tahu bahwa tipenya adalah A.

Teun D
sumber
4
Anda bisa memanggil B.GetInt () - setidaknya, Anda bisa jika tidak privat - tetapi kompilasi akan menerjemahkannya menjadi panggilan ke A.GetInt (). Cobalah!
Jon Skeet
Catatan untuk komentar Jon: visibilitas default di C # bersifat pribadi, maka contoh Anda tidak berfungsi.
Dan Rosenstark
0

Untuk tujuan saya, saya suka ide @ T-moty. Meskipun saya telah menggunakan informasi "tipe referensi sendiri" selama bertahun-tahun, merujuk kelas dasar lebih sulit dilakukan nanti.

Misalnya (menggunakan contoh @Rob Leclerc dari atas):

public class ChildA: Parent<ChildA>
{
}

public class ChildB: Parent<ChildB>
{
}

Bekerja dengan pola ini dapat menjadi tantangan, misalnya; bagaimana Anda mengembalikan kelas dasar dari pemanggilan fungsi?

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

Atau saat mengetik casting?

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

Jadi, saya mencoba menghindarinya saat saya bisa, dan menggunakannya saat harus. Jika Anda harus, saya sarankan Anda mengikuti pola ini:

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

Sekarang Anda dapat (lebih) mudah bekerja dengan file BaseClass. Namun, ada kalanya, seperti situasi saya saat ini, di mana mengekspos kelas turunan, dari dalam kelas dasar, tidak diperlukan dan menggunakan saran @ M-moty mungkin merupakan pendekatan yang tepat.

Namun, penggunaan kode @ M-moty hanya berfungsi selama kelas dasar tidak berisi konstruktor instance apa pun dalam tumpukan panggilan. Sayangnya kelas dasar saya memang menggunakan konstruktor instance.

Oleh karena itu, inilah metode ekstensi saya yang memperhitungkan konstruktor 'instance' kelas dasar:

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;
    }
}
Kabuo
sumber
0

EDIT Metode ini hanya akan berfungsi ketika Anda menyebarkan file PDB dengan executable / library, sebagai markmnl ditunjukkan kepada saya.

Jika tidak, akan menjadi masalah besar untuk dideteksi: bekerja dengan baik dalam pengembangan, tapi mungkin tidak dalam produksi.


Metode utilitas, cukup panggil metode saat Anda membutuhkannya, dari setiap tempat kode Anda:

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;
}
T-moty
sumber
1
StackTrace hanya tersedia dalam build Debug
markmnl
Tidak benar: StackTrace akan tersedia saat Anda juga menerapkan file .pdb dalam mode rilis. stackoverflow.com/questions/2345957/…
T-moty
Saya mengerti maksud Anda. Tidak dapat diterima bahwa suatu metode hanya berfungsi ketika file PDB diterapkan. Saya akan mengedit jawabannya
T-moty