.NET: Statik yönteminde "bu" sınıfın türünü belirleyin


96

Statik olmayan bir yöntemde kullanabilirdim this.GetType()ve Type. Aynı şeyi Typestatik bir yöntemde nasıl elde edebilirim ? Tabii ki, ben sadece yazamıyorum typeof(ThisTypeName)çünkü ThisTypeNamesadece çalışma zamanında bilinmektedir. Teşekkürler!


16
STATIC bağlamındasınız ve typeof (ThisTypeName) yazamıyor musunuz? Nasıl?
Bruno Reis

1
Statik bir yöntemin içinde 'çalışma zamanı' gibisi yoktur (statik bir yönteme iletilen bir bağımsız değişken hakkında konuşmadığınızı varsayarsak). Bu durumda typeof (RelevantType) diyebilirsiniz.
Manish Basantani

2
Statik bir yöntem sanal olamaz. Türü zaten biliyorsun.
Hans Passant

7
Soyut bir sınıftan birçok türetilmiş sınıf olacaktır. Temel soyut sınıf, statik sözlüğe sahiptir <Int, Type>. Bu nedenle türetilmiş sınıflar, kendilerini statik oluşturuculara "kaydeder" (dic.Add (N, T)). Ve evet, türünü biliyorum :) Biraz tembelim ve metni değiştirmeyi sevmiyorum ve çalışma zamanında "T" nin belirlenip belirlenemeyeceğini merak ediyordum. Lütfen yalanımı bağışlayın, çünkü soruyu basitleştirmek gerekiyordu. Ve işe yaradı;) Şimdi kabul edilen bir çözüm var. Teşekkürler.
Yegor

Bir alt sınıf, üst sınıfının statik yöntemlerini miras alır, değil mi? Bir süper sınıf statik yöntemin tüm alt sınıfları için yararlı olması mantıklı olmaz mı? Statik, basitçe bir örnek olmadan anlamına gelir, kesinlikle ortak bir temel sınıftaki ortak kod ilkesi, örnek yöntemlerinin yanı sıra statik yöntemler için de geçerlidir?
Matt Connolly

Yanıtlar:


135

this.GetType()Statik yöntemlere eşdeğer 1 astar arıyorsanız , aşağıdakileri deneyin.

Type t = MethodBase.GetCurrentMethod().DeclaringType

Bu muhtemelen kullanmaktan çok daha pahalı olsa da typeof(TheTypeName).


1
Bu iyi çalışıyor. Teşekkürler :) O kadar pahalı değil çünkü oldukça nadir denecek.
Yegor

2
Giriş, "pahalı" Jared, işlemci için maliyetli oldukları anlamına gelir, genellikle yavaş anlamına gelir. Ama "çok daha pahalı" demek, daha yavaş demek. Bir roket güdüm sistemi tasarlamadığınız sürece, muhtemelen hiç yavaş değil.
Dan Rosenstark

1
GetCurrentMethod'un bazı ciddi performans sorunlarına neden olduğunu gördüm. Ancak türü henüz aldığınız için onu önbelleğe alabilirsiniz.
Jonathan Allen

52
Bu her zaman alt sınıflar söz konusu olduğunda çağrıldığı sınıfı değil, geçerli yöntemi uygulayan sınıfı döndürür.
Matt Connolly

3
Kodun farklı sınıf adlarına veya başka bir şeye taşınması durumunda hatalardan kaçınmak kullanışlı olur, ancak iyi bir yeniden düzenleme aracı typeof(TheTypeName)yine de ilgilenmelidir .
Nyerguds

59

Diğer yanıtların tam olarak açıklığa kavuşturmadığı bir şey var ve bu tür fikrinizin yalnızca uygulama sırasında mevcut olmasıyla ilgili.

Statik bir üye yürütmek için türetilmiş bir tür kullanırsanız , ikili dosyada gerçek tür adı atlanır. Örneğin, bu kodu derleyin:

UnicodeEncoding.GetEncoding(0);

Şimdi ildasm'ı kullanın ... aramanın şöyle yapıldığını göreceksiniz:

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

Derleyici çağrıyı çözdü Encoding.GetEncoding- UnicodeEncodingsol iz yok . Korkarım bu, "şu anki tip" fikrinizi anlamsız kılıyor.


10 yıl ileri sarın ve neden C #'da hala sanal statik yok? ;) genellikle bunlara ihtiyaç olmazdı ... ama işe yarayacakları nadir durumlar vardır;)
mart

@marchewek: C # ekibinin her zaman yapacak daha yararlı şeyler bulmasına yetecek kadar nadir olduğunu söyleyebilirim. (Bunca zamandır boşta
değildiler

24

Başka bir çözüm de kendi kendine başvuran bir tür kullanmaktır

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

Sonra onu devralan sınıfta, kendine referans veren bir tür yapıyorum:

public class Child: Parent<Child>
{
}

Şimdi, Parent içindeki typeof (TSelfReferenceType) çağrı türü, bir örneğe ihtiyaç duymadan arayanın Türünü alacak ve döndürecektir.

Child.GetType();

-Rob


Bunu tekil kalıplar için kullandım, yani Singleton <T> ... statik üyeler daha sonra hata mesajlarında veya ihtiyaç duyulan başka yerlerde typeof (T) 'ye başvurabilir.
yoyo

1
Selam. Bu yanıtı gerçekten beğendim ve takdir ediyorum çünkü bu bana statik temel işlevden çocuk türünü bulmam için bir çözüm sağlıyor.
Bill Software Engineer

1
Güzel. C # 'da bunun bu küçük kod kopyası olmadan yapılamayacağı çok üzücü.
Görünen Ad

Bu geçici çözümün başka bir örneği stackoverflow.com/a/22532416/448568
Steven de Salas

Her ikisi de (bu ve Steven tarafından bağlanan) temel sınıfın bir uygulayıcısının mirasçıları için çalışmayacaktır ... Torunlar Çocuk türü ile sonuçlanacaktır ... Çok kötü C # sanal statiğe sahip değildir;)
marchewek

5

thisStatik bir yöntemde kullanamazsınız , bu nedenle bu doğrudan mümkün değildir. Ancak, bir nesnenin türüne ihtiyacınız varsa GetType, onu çağırın ve thisörneği geçirmeniz gereken bir parametre yapın, örneğin:

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

Yine de bu kötü bir tasarım gibi görünüyor. Örneğin türünü gerçekten kendi statik yönteminin içine almanız gerektiğinden emin misiniz? Bu biraz tuhaf görünüyor. Neden sadece bir örnek yöntemi kullanmıyorsunuz?

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

3

Typeof (ThisTypeName) 'i neden kullanamadığınızı anlamıyorum. Bu genel olmayan bir türse, bu işe yaramalıdır:

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

Genel bir türse, o zaman:

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

Burada bariz bir şeyi mi kaçırıyorum?


7
Foo'dan türetilmiş bir sınıf Bar oluşturursanız ve sonra sınıf Method1'i miras alırsa bu işe yaramaz - o zaman Bar.Method1'e yapılan bir çağrı typeof (Foo) 'yu işleyecektir ki bu yanlıştır. Miras alınan Method1, bir şekilde Bar'da kullanıldığını bilmeli ve sonra typeof (Bar) 'ı almalıdır.
JustAMartin

0

Üyeniz statik olduğunda, çalışma zamanında hangi türün parçası olduğunu her zaman bilirsiniz. Bu durumda:

class A
{
  public static int GetInt(){}

}
class B : A {}

Çağrı yapamazsınız (düzenleme: görünüşe göre, aşağıdaki yorumu görebilirsiniz, ancak yine de A'yı arıyor olacaksınız):

B.GetInt();

üye statik olduğu için kalıtım senaryolarında rol oynamaz. Ergo, türünün A olduğunu her zaman bilirsin.


4
Sen olabilir , özel olmasaydı yapabildin, en azından - - B.GetInt () diyoruz ama derleme A.GetInt bir çağrı çevirecek (). Dene!
Jon Skeet

Jon'un yorumuna dikkat edin: C #'daki varsayılan görünürlük özeldir, bu nedenle örneğiniz çalışmaz.
Dan Rosenstark

0

Amaçlarım için, @ T-moty'nin fikrini beğendim. Yıllardır "kendi kendine referans veren tip" bilgisini kullanmış olsam da, temel sınıfa atıfta bulunmak daha sonra daha zor.

Örneğin (yukarıdaki @Rob Leclerc örneğini kullanarak):

public class ChildA: Parent<ChildA>
{
}

public class ChildB: Parent<ChildB>
{
}

Bu modelle çalışmak zor olabilir, örneğin; temel sınıfı bir işlev çağrısından nasıl döndürürsünüz?

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

Veya tip döküm ne zaman?

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

Bu yüzden, elimden geldiğince kaçınmaya çalışıyorum ve gerektiğinde kullanıyorum. Gerekirse, şu kalıbı izlemenizi öneririm:

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

Artık (daha fazla) ile kolayca çalışabilirsiniz BaseClass. Bununla birlikte, benim mevcut durumum gibi, türetilmiş sınıfı temel sınıf içinden açığa çıkarmanın gerekli olmadığı ve @ M-moty'nin önerisini kullanmanın doğru yaklaşım olabileceği zamanlar vardır.

Ancak, @ M-moty'nin kodunu kullanmak, yalnızca temel sınıf çağrı yığınında herhangi bir örnek oluşturucu içermediği sürece çalışır. Maalesef benim temel sınıflarım örnek oluşturucuları kullanıyor.

Bu nedenle, temel sınıf 'örnek' oluşturucularını hesaba katan benim genişletme yöntemim:

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

DÜZENLE Bu yöntemler yalnızca PDB dosyalarını çalıştırılabilir / kitaplık ile dağıttığınızda, markmnl'nin bana işaret ettiği gibi çalışır .

Aksi takdirde tespit edilmesi gereken çok büyük bir sorun olacaktır: geliştirmede iyi çalışıyor, ancak üretimde olmayabilir.


Fayda yöntemi, ihtiyacınız olduğunda, kodunuzun her yerinden yöntemi çağırmanız yeterlidir:

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 yalnızca Hata Ayıklama sürümlerinde kullanılabilir
markmnl

Doğru değil: .pdb dosyalarını yayın modunda dağıttığınızda da StackTrace kullanılabilir. stackoverflow.com/questions/2345957/…
T-moty

Ne demek istediğini anladım. Bir yöntemin yalnızca PDB dosyaları konuşlandırıldığında çalışması kabul edilemez. Cevabı düzenleyeceğim
T-moty
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.