GetType () yalan söyleyebilir mi?


94

Birkaç gün önce SO: GetType () ve polimorfizm'de birkaç gün önce sorulan şu soruya dayanarak ve Eric Lippert'in cevabını okuyarak , yapmanın GetType()sanal olmaması gerçekten bir nesnenin onun hakkında yalan söyleyemeyeceğini düşünmeye başladım Type.

Eric'in cevabı özellikle şu şekildedir:

Çerçeve tasarımcıları, bir nesnenin türü hakkında yalan söylemesine izin vererek, onu aynı türdeki diğer üç yöntemle tutarlı hale getirmek için inanılmaz derecede tehlikeli bir özellik eklemeyecekler.

Şimdi soru şudur: Bir nesneyi yapabilirsiniz yapar hemen belli olmadan kendi türü hakkında yalan? Burada son derece yanılıyor olabilirim ve durum buysa açıklamayı çok isterim, ancak aşağıdaki kodu göz önünde bulundurun:

public interface IFoo
{
    Type GetType();
}

Ve söz konusu arayüzün aşağıdaki iki uygulaması:

public class BadFoo : IFoo
{
    Type IFoo.GetType()
    {
        return typeof(int);
    }
}

public class NiceFoo : IFoo
{
}

Ardından aşağıdaki basit programı çalıştırırsanız:

static void Main(string[] args)
{
    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    Console.ReadLine();
}

Yeterince emin badFoobir hatalı çıktı verir Type.

Şimdi, Eric'in bu davranışı " inanılmaz derecede tehlikeli bir özellik " olarak tanımlamasına dayanarak bunun ciddi sonuçları olup olmadığını bilmiyorum , ancak bu model inandırıcı bir tehdit oluşturabilir mi?


3
ilginç başlık ve konu!
David

43
IFoo.GetTypeve object.GetTypeaynı şey değil, bu yüzden burada kötü tarz dışında kötü bir şey olmuyor. Düzenleme: Genellikle GetTypederleme sırasında bilinmeyen bazı nesnelerde, çoğu durumda objectbazı tehlikeli arayüzlerde çağrılır . :)
leppie

4
Unvanınız efendim, günümü yarattı.
Soner Gönül

5
Siz sadece aynı imzaya sahip yeni üyeleri GetTypetanıtın. Bu GetType, önemli olan yöntemle ilgili değildir . Değiştirici anahtar sözcüğünü GetTypekullanarak ilgili yöntemi gizleyen bir genel örnek yöntemi de oluşturabilirsiniz new. Not, static Type Test<T>(T t) { return t.GetType(); }(üzerinde kısıtlama olmadan T) gibi genel bir yönteminiz varsa , benzer şeyler Test<IFoo>(new BadFoo())yine de orijinal GetTypeyöntemi çağıracaktır .
Jeppe Stig Nielsen

2
@Jamiec - "Bu model inandırıcı bir tehdit oluşturabilir mi?" retorik değil.
Martin Smith

Yanıtlar:


45

Güzel soru! Gördüğüm kadarıyla, bir geliştiriciyi ancak GetType nesne üzerinde sanal olsaydı gerçekten yanlış yönlendirebilirdin, ki öyle değil.

Yaptığınız şey, GetType'ı şu şekilde gölgelendirmeye benzer:

public class BadFoo
{
    public new Type GetType()
    {
        return typeof(int);
    }
}

bu sınıfla (ve GetType () yöntemi için MSDN'deki örnek kodu kullanarak ) gerçekten sahip olabilirsiniz:

int n1 = 12;
BadFoo foo = new BadFoo();

Console.WriteLine("n1 and n2 are the same type: {0}",
                  Object.ReferenceEquals(n1.GetType(), foo.GetType())); 
// output: 
// n1 and n2 are the same type: True

öyleyse, yikes, başarıyla yalan söyledin, değil mi? Evet ve hayır ... Bunu bir istismar olarak kullanmanın, BadFoo örneğinizi, bir objectnesneler hiyerarşisi için muhtemelen bir veya ortak bir temel tür bekleyen bir yöntem için argüman olarak kullanmak anlamına geleceğini düşünün . Bunun gibi bir şey:

public void CheckIfInt(object ob)
{
    if(ob.GetType() == typeof(int))
    {
        Console.WriteLine("got an int! Initiate destruction of Universe!");
    }
    else
    {
        Console.WriteLine("not an int");
    }
}

ancak CheckIfInt(foo)"int değil" yazdırır.

Yani, temelde (örneğinize dönecek olursak), gerçekten yalnızca birinin sizin IFooarayüzünüze yazdığı kodla "yalancı tipinizden" yararlanabilirsiniz , ki bu onun "özel" bir GetType()metoda sahip olduğu gerçeği hakkında çok açık bir durumdur .

Yalnızca GetType () nesne üzerinde sanal ise CheckIfInt, başkası tarafından yazılmış kitaplıklarda tahribat yaratmak için yukarıdaki gibi yöntemlerle kullanılabilecek bir "yalancı" tip oluşturabilirdiniz.


evet, gölgeleme ile tamamen aynı. Son paragraf, gerçekten bir tehdit olmadığını açıkça ortaya koyan şeydir. Teşekkürler!
2013

32

Tür konusunda emin olmanın iki yolu vardır:

  1. typeofAşırı yüklenemeyen Tip üzerinde kullanın

    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    
    Console.WriteLine("BadFoo really is a '{0}'", typeof(BadFoo));
    Console.WriteLine("NiceFoo really is a '{0}'", typeof(NiceFoo));
    Console.ReadLine();
    
  2. Örneği bir'e dönüştürün objectve GetType()Yöntemi çağırın

    IFoo badFoo = new BadFoo();
    IFoo niceFoo = new NiceFoo();
    
    Console.WriteLine("BadFoo says he's a '{0}'", badFoo.GetType().ToString());
    Console.WriteLine("NiceFoo says he's a '{0}'", niceFoo.GetType().ToString());
    
    Console.WriteLine("BadFoo really is a '{0}'", ((object)badFoo).GetType());
    Console.WriteLine("NiceFoo really is a '{0}'", ((object)niceFoo).GetType());
    Console.ReadLine();
    

1
typeofYalnızca IFoo badFooparametre olarak alan bir yöntemi nasıl kullanacaksınız ?
huysentruitw

typeofburada yapmamız gereken bir sınıfın örneklerine uygulanamaz. Tek seçeneğiniz GetType().
2013, 09:12

İki örneğiniz iki farklı şey yapıyor. İkinci satırlar gerekli soruyu yanıtlamaz - açıkça "türü BadFooalırlar" ancak "değişkenin türünü almazlar" badFoo.
Dan Puzey

Evet üzgünüm Her zamanki gibi soruyu yeterince dikkatli okumadım. Cevabımı, türden emin olmanın iki farklı yolunu işaret edecek şekilde güncelledim.
Johannes Wanzek

1
Bu benim işaret ettiğim şeydi. Öyleyse yorumunuzun amacı nedir? :)
Johannes Wanzek

10

Hayır, GetType'ı yalan söyleyemezsiniz. Yalnızca yeni bir yöntem tanıtıyorsunuz. Yalnızca bu yöntemin farkında olan kod onu çağıracaktır.

Örneğin, üçüncü taraf veya çerçeve kodunun gerçek olan yerine yeni GetType yönteminizi çağırmasını sağlayamazsınız, çünkü bu kod, yönteminizin var olduğunu bilmez ve bu nedenle onu asla çağırmaz.

Bununla birlikte, kendi geliştiricilerinizi böyle bir beyanla karıştırabilirsiniz. Bildiriminizle derlenen ve IFoo olarak yazılan parametreleri veya değişkenleri veya bundan türetilen herhangi bir türü kullanan herhangi bir kod, bunun yerine yeni yönteminizi kullanacaktır. Ancak bu yalnızca kendi kodunuzu etkilediği için gerçekten bir "tehdit" oluşturmaz.

Bir sınıf için özel bir tür açıklaması sağlamak istiyorsanız, bu , belki de sınıfınıza TypeDescriptionProviderAttribute ile açıklama ekleyerek , bir Özel Tür Tanımlayıcısı kullanılarak yapılmalıdır . Bu, bazı durumlarda faydalı olabilir.


2
Üçüncü taraf kodunun özel bir GetType uygulaması hakkında bilgi sahibi olmadığını açıkça belirten ikinci paragrafınız için +1. Diğer cevaplar bu fikre işaret ediyordu ama gerçekten ortaya çıkıp onu söylemedi (en azından o kadar net değil).
brichins

7

Eh, aslında orada olduğunu zaten yalan bir tür GetTypeherhangi null türü:.

Bu kod :

int? x = 0; int y = 0;
Console.WriteLine(x.GetType() == y.GetType());

çıktılar True.


Aslında öyle değil int?yalan söylüyor, sadece örtülü cast objectdönüşler int?bir kutulu içine int. Ama yine de sen olamaz söylemek int?dan intile GetType().


1
Beklenen (veya en azından iyi bilinen) davranış. Bu sorunun cevapları , bu kavramı ve sebebini oldukça açık bir şekilde açıklamaktadır.
brichins

@brichins: Bence Bilindiği katılıyorum, ama ben öyle kabul edemez iyi -bilinen. Her neyse, bu GetType()biraz garip sonuç veren bir durum . Aslında birkaç meslektaşıma gölgesiz GetType()bir nesnenin gerçek nesnenin çalışma zamanı türünden farklı bir şey döndürüp döndüremeyeceğini sordum , herkesin cevabı 'hayır' oldu.
Vlad

5

GetType'ı çağıran her kütüphane kodu değişkeni 'Nesne' veya Genel tip 'T' olarak ilan edeceği için bunun olacağını sanmıyorum

Aşağıdaki kod:

    public static void Main(string[] args)
    {
        IFoo badFoo = new BadFoo();
        IFoo niceFoo = new NiceFoo();
        PrintObjectType("BadFoo", badFoo);
        PrintObjectType("NiceFoo", niceFoo);
        PrintGenericType("BadFoo", badFoo);
        PrintGenericType("NiceFoo", niceFoo);
    }

    public static void PrintObjectType(string actualName, object instance)
    {
        Console.WriteLine("Object {0} says he's a '{1}'", actualName, instance.GetType());
    }

    public static void PrintGenericType<T>(string actualName, T instance)
    {
        Console.WriteLine("Generic Type {0} says he's a '{1}'", actualName, instance.GetType());
    }

baskılar:

BadFoo nesnesi onun bir 'TypeConcept.BadFoo' olduğunu söylüyor

NiceFoo nesnesi onun bir 'TypeConcept.NiceFoo' olduğunu söylüyor

Genel Tip BadFoo, 'TypeConcept.BadFoo' olduğunu söylüyor

Jenerik Tip NiceFoo, 'TypeConcept.NiceFoo' olduğunu söylüyor

Bu tür bir kodun kötü senaryolarla sonuçlanacağı tek zaman, parametre türünü IFoo olarak bildirdiğiniz kendi kodunuzdadır.

    public static void Main(string[] args)
    {
        IFoo badFoo = new BadFoo();
        IFoo niceFoo = new NiceFoo();
        PrintIFoo("BadFoo", badFoo);
        PrintIFoo("NiceFoo", niceFoo);
    }

    public static void PrintIFoo(string actualName, IFoo instance)
    {
        Console.WriteLine("IFoo {0} says he's a '{1}'", actualName, instance.GetType());
    }

IFoo BadFoo, 'System.Int32' olduğunu söylüyor

IFoo NiceFoo, 'TypeConcept.NiceFoo' olduğunu söylüyor


4

Söyleyebileceğim kadarıyla olabilecek en kötü şey, zehirli sınıfı kullanan yanıltıcı masum programcılardır, örneğin:

Type type = myInstance.GetType();
string fullName = type.FullName;
string output;
if (fullName.Contains(".Web"))
{
    output = "this is webby";
}
else if (fullName.Contains(".Customer"))
{
    output = "this is customer related class";
}
else
{
    output = "unknown class";
}

Eğer myInstancesöz konusu açıklamak gibi bir sınıfın örneğidir, sadece bilinmeyen türü olarak kabul edilir.

Yani cevabım hayır, burada gerçek bir tehdit göremiyorum.


1
Elbette. Dikkatli bir programcı derleme sırasında hangi "GetType" yöntemini çağırdığını görebilir. Object.GetType()farklıdır SomeUserdefinedInterfaceClassOrStruct.GetType(). Sadece, dynamictürü kullanırsanız, bağlama zamanında ne olacağını asla bilemezsiniz. Yani böyle dynamic x = expression; ... Type t = ((object)x).GetType();durumlarda kullanmalısın .
Jeppe Stig Nielsen

@Jeppe fuar noktaları! Sanırım ayrı bir cevabı haklı çıkarıyor, cevabım daha çok "masum" programcıya odaklanıyor ve bu kadar dikkatli olmayacak.
Gölge Sihirbazı Sizin İçin Kulak

3

Bu tür bir saldırıya karşı güvenli oynamak istiyorsanız bazı seçenekleriniz var:

Önce nesneye yayınlayın

Orijinal GetType()yöntemi ilk önce örneği bir şuna çevirerek çağırabilirsiniz object:

 Console.WriteLine("BadFoo says he's a '{0}'", ((object)badFoo).GetType());

sonuçlanır:

BadFoo says he's a 'ConsoleApplication.BadFoo'

Şablon yöntemini kullanın

Bu şablon yöntemini kullanmak size gerçek türü de verecektir:

static Type GetType<T>(T obj)
{
    return obj.GetType();
}

GetType(badFoo);

2

Arasında bir fark vardır object.GetTypeve IFoo.GetType. GetTypebilinmeyen nesnelerde derleme zamanında çağrılır, Arabirimler üzerinde değil. Örneğinizde çıktı ilebadFoo.GetType size yöntemini aşırı çünkü, bahaviour bekleniyor. Tek sorun, diğer programcıların bu davranıştan kafalarının karışmasıdır.

Ancak kullanırsanız typeof(), türün aynı olduğu sonucunu verir ve üzerine yazamazsınız typeof().

Ayrıca programcı derleme zamanında hangi yöntemi GetTypeçağırdığını görebilir.

Öyleyse sorunuza göre: Bu kalıp inandırıcı bir tehdit oluşturmaz, ancak aynı zamanda en iyi kodlama tarzı değildir.


badFoo.GetType()IS beklenen davranış, çünkü GetTypeaşırı yüklendi.
huysentruitw
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.