GetType () можа ляжаць?

На аснове наступнага пытання спытаў некалькі дзён таму ў SO: GetType() і палімарфізм і чытанне <�а HREF = "https://stackoverflow.com/questions/16679239/gettype-and-polymorphism/16681120»> адказ Эрык Липперта, я пачаў думаць, калі зрабіць GetType() не можа быць віртуальным сапраўды гарантаваў, што аб'ект не можа хлусіць аб сваім Тып .

У прыватнасці, адказ Эрыка сцвярджае наступнае:

<�Р> Каркасныя дызайнеры не збіраюцца дадаць неверагодна небяспечную функцыю, такія як дазвол аб'екта хлусіць аб сваім тыпе проста зрабіць гэта ў адпаведнасці з трыма іншымі метадамі на той жа тып.

Цяпер пытанне: ці магу я зрабіць аб'ект, робіць хлусня аб яго тыпе без яго адразу відаць? Можа быць, я глыбока няправільна тут, і я хацеў бы растлумачыць, калі гэта так, але разгледзім наступны код:

public interface IFoo
{
    Type GetType();
}

А наступныя дзве рэалізацыі названага інтэрфейсу:

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

public class NiceFoo : IFoo
{
}

Затым, калі вы запусціце наступную простую праграму:

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

Вядома badFoo выводзіць памылковы Тып .

Цяпер я не ведаю, калі гэта мае якія-небудзь сур'ёзныя наступствы, заснаваныя на Эрыку, якія апісваюць гэтыя паводзіны як « неверагодна небяспечна функцыя ", але можа гэтая мадэль уяўляе рэальную пагрозу?

93
GetType() гэта хлусня!
дададзена аўтар Nahum, крыніца
цікавае назву і тэма!
дададзена аўтар David, крыніца
<�Код> IFoo.GetType і object.GetType ня не тое ж самае, так што нічога дрэннага, што адбываецца тут, за выключэннем дрэннага стылю. Edit: Звычайна GetType будзе называцца на які-небудзь аб'ект не вядомы падчас кампіляцыі, у большасці выпадкаў аб'екта , а не нейкі мудрагелісты інтэрфейс. :)
дададзена аўтар leppie, крыніца
@MartinSmith - узяты ў асобнасці, вы маеце рацыю. Але ўсё пытанне напрошваецца сам пытанне, які з'яўляецца рытарычным ИМО.
дададзена аўтар Jamiec, крыніца
Гэта рытарычнае пытанне , так верб галасавала, каб закрыць, як перадачу N ARQ (як Thats ў апісанні перадачу N ARQ)
дададзена аўтар Jamiec, крыніца
Ваш тытул сэр, зрабіў мой дзень.
дададзена аўтар Soner Gönül, крыніца
@leppie Мех ... Я толькі заўважыў, што ваш каментар ўжо патлумачыў кропку я спрабаваў зрабіць у маёй абароне больш выразна і толькі два з паловай радкоў тэксту
дададзена аўтар Paolo Falabella, крыніца
Вы проста ўвесці новы члены таксама называюць GetType , з той жа подпісам. Гэта не адносіцца да GetType метад, які мае важнае значэнне. Вы можаце таксама зрабіць публічны метад асобніка, які хавае адпаведны GetType метад, выкарыстоўваючы новы мадыфікатар ключавых слоў. Звярніце ўвагу, калі ў вас ёсць агульны метад, як статычныя выпрабаванні тыпу (T т) {вярнуцца t.GetType (); } (без абмежавання на T ), то такія рэчы, як Test (новы BadFoo ()) будзе па-ранейшаму называюць арыгінальны GetType метад.
дададзена аўтар Jeppe Stig Nielsen, крыніца
@Jamiec - пытанне «можа гэтая мадэль уяўляе рэальную пагрозу?» не рытарычны.
дададзена аўтар Martin Smith, крыніца
@leppie: Allthough асноўным я згодны з вамі, ёсць выпадкі, калі вы маглі б быць пераборы IFoo надрукаваў калекцыю, а затым на аснове рэальнага тыпу аб'екта павінны зрабіць нешта канкрэтнае да гэтага тыпу, калекцыя можа вельмі розныя кліенты адлюстроўваюць аб'екты, якія маюць няма агульнасць забароны інтэрфейсу. Я ведаю, што гэта не пажаданая мадэль, і яе верагодны дрэнны код пах, але ў некаторых выпадках вы затрымаліся з ім.
дададзена аўтар InBetween, крыніца
@ V4Vendetta: Гэты код не дае ніякіх папярэджанняў
дададзена аўтар InBetween, крыніца
З вашага кода, паставіўшы курсор мышы над GetType дае падказку пра « Тып IFoo.GetType() ». Я не ведаю, калі гэта адпавядае вашым крытэрам «без яго адразу бачна».
дададзена аўтар AakashM, крыніца
Калі вы робіце тое ж самае, але назваць GetType() з імі быць аб'ект s, а не IFoo s, гэта ўсё-ткі атрымаць шанец ляжаць?
дададзена аўтар Patashu, крыніца
<�Код> ((аб'ект) badFoo) .GetType() гэта адзіны спосаб быць упэўненым, што тып аб'екта.
дададзена аўтар Johannes Wanzek, крыніца

8 адказы

Добры пытанне! Як я бачу, вы толькі маглі сапраўды прывесці распрацоўшчык субрата, калі GetType быў віртуальным на аб'ект, які ён не з'яўляецца.

Тое, што вы зрабілі гэта падобна зацяненне GetType, як гэта:

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

з гэтым класам (і з выкарыстаннем ўзор кода з MSDN для GetType() метад </а>) вы можаце сапраўды ёсць:

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

так, Плясь, вы паспяхова схлусіў, ці не так? Ну так, і няма ... Лічаць, што выкарыстоўваць гэта як эксплойт будзе азначаць выкарыстанне асобніка BadFoo ў якасці аргументу метаду недзе, што чакае, хутчэй за ўсё, у аб'ект або агульнай базавай тыпу для іерархіі аб'екты. Нешта накшталт гэтага:

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

але CheckIfInt (Foo) друкуе "не з'яўляецца ИНТ".

Такім чынам, у асноўным (на ваш прыклад), вы маглі б сапраўды толькі выкарыстаць свой «лежачы тыпу» з кодам, што нехта напісаў супраць вашага IFoo інтэрфейс, які вельмі выразна пра тое, што ён мае «звычай " GetType() метад.

Толькі калі GetType() быў віртуальным на аб'екце вы маглі б выпрацаваць «лежачы» тып, які можа быць выкарыстаны з метадамі, як CheckIfInt вышэй, каб стварыць хаос у бібліятэках, напісаных кімсьці іншым.

45
дададзена
так, яго сапраўды гэтак жа, як і зацяненне. Апошні пункт з'яўляецца тое, што на самой справе робіць гэта відавочным, што няма нават ніякай пагрозы. Дзякуй!
дададзена аўтар InBetween, крыніца

Ёсць два спосабу быць упэўнены Тып:

  1. Use typeof on the Type which can't be overloaded

    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. Cast the instance to an object and call the GetType() Method

    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();
    
32
дададзена
Вядома вы «ўпэўненыя» пра тып з TYPEOF , так як вы відавочна надаўшы яму тып. кропка пытанне задае спасылку на які вы не ведаеце, фактычны тып, які тып гэта. другі прыклад лепш падыходзіць.
дададзена аўтар Dave Cousineau, крыніца
Вашыя два ўзору робяць дзве розныя рэчы. Другія лініі не даюць адказу на пытанне патрабуецца - яны відавочна «атрымаць тып BadFoo », але яны не «атрымаць тып зменнай badFoo ».
дададзена аўтар Dan Puzey, крыніца
Ваша праўка добра.
дададзена аўтар Dan Puzey, крыніца
Як вы будзеце выкарыстоўваць TYPEOF ў метадзе, які толькі атрымлівае IFoo badFoo у якасці параметру?
дададзена аўтар Wouter Huysentruit, крыніца
<�Код> TypeOf не можа быць ужыты да асобнікам класа, які з'яўляецца тым, што нам трэба зрабіць тут. Ваш адзіны варыянт GetType() .
дададзена аўтар InBetween, крыніца
Гэта тое, што я паказваў. Дык які сэнс вашага каментара? :)
дададзена аўтар Johannes Wanzek, крыніца
Так, я прашу прабачэння. Як звычайна, я не чытаў гэтае пытанне дастаткова старанна. Я абнавіў свой адказ, каб паказаць на два розных спосабу быць упэўнены тыпу.
дададзена аўтар Johannes Wanzek, крыніца
паглядзіце на маё рэдагаванне;)
дададзена аўтар Johannes Wanzek, крыніца

Не, вы не можаце зрабіць GetType хлусня. Вы толькі ўводзіць новы метад. Толькі код, ведае аб гэтым метадзе будзе называць яго.

Вы не можаце, напрыклад, зрабіць трэцюю бок або рамачны код называюць свой новы метад GetType замест рэальнага, так як гэты код не ведае, што ваш метад існуе і будзе таму ніколі не называе.

Аднак вы можаце пераблытаць свае ўласныя распрацоўшчык з такой дэкларацыяй. Любы код, які складаецца з дэкларацыяй і што параметры выкарыстання або зменнымі, уведзеныя ў якасці IFoo або любога тыпу, вытворнага ад гэтага сапраўды будзе выкарыстоўваць новы метад. Але так як гэта ўплывае толькі на свой уласны код, ён на самай справе не накладае «пагрозу».

Калі вы хочаце, каб забяспечыць апісанне карыстацкага тыпу для класа гэта павінна быць зроблена з дапамогай карыстацкі тып дэскрыптар , магчыма, анатаваньня вашага класа з TypeDescriptionProviderAttribute . Гэта можа быць карысна ў некаторых сітуацыях.

10
дададзена
+1 для вашага другога пункта відавочна паказвае на тое, што трэці бок кода не ведае аб рэалізацыі карыстацкіх GetType. Іншыя адказы намякаюць на гэтую ідэю, але на самой справе не выйсці і сказаць, што (прынамсі, не так ясна).
дададзена аўтар brichins, крыніца

Ну, на самай справе ёсць з'яўляецца ужо тып, які можа знаходзіцца ў GetType : любы обнулять тыпу.

This code:

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

выхады True .


На самай справе, гэта не Int? , які ляжыць, толькі невідавочнае прывядзенне да аб'екта атрымліваецца Int? у штучны ўпакоўцы Int </код >. Але тым не менш вы не пазначыць Int? з Int з GetType() .

6
дададзена
@brichins: Ну, я згодны, што гэта вядома, але я не магу пагадзіцца, што гэта <�я> а </я> -known. Ва ўсякім разе, гэта той выпадак, дзе GetType() вырабляе некалькі дзіўны вынік. На самай справе я задаў некалькі калег аб тым, ці з'яўляецца незатенённым GetType() можа вярнуць тое, што адрозніваецца ад тыпу выканання фактычнага аб'екта, адказ кожнага быў «не».
дададзена аўтар Vlad, крыніца
Які чаканае паводзіны (ці, па меншай меры, добра вядомыя). Адказы на гэтае пытанне тлумачыць гэтую канцэпцыю даволі выразна, а таксама прычыну гэтага.
дададзена аўтар brichins, крыніца

Я не думаю, што гэта будзе, бо ўсе бібліятэкі кода, які выклікае GetType аб'явім зменную як «аб'ект», або ў якасці радавога тыпу «Т»

Наступны код:

    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(string actualName, T instance)
    {
        Console.WriteLine("Generic Type {0} says he's a '{1}'", actualName, instance.GetType());
    }

друкуе:

<�Р> Аб'ект BadFoo кажа, што ён 'TypeConcept.BadFoo'      <�Р> Аб'ект NiceFoo кажа, што ён 'TypeConcept.NiceFoo'      <�Р> Радавой тып BadFoo кажа, што ён 'TypeConcept.BadFoo'      <�Р> Радавой тып NiceFoo кажа, што ён 'TypeConcept.NiceFoo'

Адзіны раз, калі гэты від коды прывядзе да дрэннага сцэнары знаходзіцца ў вашым ўласным кодзе, дзе вы аб'яўляеце тып параметру як IFoo

    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'      <�Р> IFoo NiceFoo кажа, што ён 'TypeConcept.NiceFoo'
5
дададзена

Горш, што можа здарыцца, наколькі я магу сказаць, гэта ўводзіць у зман нявінных праграмістаў, якія вырашылі выкарыстаць атручаную клас, напрыклад:

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

Калі MyInstance з'яўляецца асобнікам класа, такімі, як вы апісалі ў пытанні, ён проста будзе разглядацца як невядомы тыпу.

Так што мой адказ не, не можа ўбачыць рэальную пагрозу тут.

4
дададзена
@Jeppe справядлівых балаў! Я думаю, што гэта апраўдвае асобны адказ, мой адказ засяродзіцца на «нявінны» праграміст, які не будзе так старанна.
дададзена аўтар Shadow Wizard, крыніца
Вядома. Пільны праграміст можа бачыць падчас кампіляцыі, які метад «GetType» ён заклікае. <�Код> Object.GetType() адрозніваецца ад SomeUserdefinedInterfaceClassOrStruct.GetType() . Толькі, калі вы выкарыстоўваеце <�дынамічны код /> тыпу, вы ніколі не можаце ведаць, што будзе адбывацца ў што звязвае час. Такім чынам, вы павінны выкарыстоўваць дынамічны х = выраз; ... Тып т = ((аб'ект) х) .GetType (); у падобных выпадках.
дададзена аўтар Jeppe Stig Nielsen, крыніца

У вас ёсць некалькі варыянтаў, калі вы хочаце гуляць у бяспекі супраць такога ўзлому:

<�Моцны> Cast аб'екта першая </моцны>

Вы можаце назваць арыгінальны GetType() метад, з дапамогай першага ліцця асобніка да аб'екта :

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

Вынікі ў:

BadFoo says he's a 'ConsoleApplication.BadFoo'

<�Моцны> Выкарыстоўваць метад шаблон

Выкарыстоўваючы гэты метад шаблону таксама дасць вам рэальны тып:

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

GetType(badFoo);
3
дададзена

Існуе розніца паміж object.GetType і IFoo.GetType . <�Код> GetType выклікаецца падчас кампіляцыі на невядомых аб'ектаў, якія не на інтэрфейсах. У вашым прыкладзе, з выхадам badFoo.GetType чакаецца bahaviour, таму што перагрузіць метад. Толькі справа ў тым, што іншыя праграмісты могуць заблытацца такімі паводзінамі.

Але калі вы выкарыстоўваеце TypeOf() гэта будзе выхад, што тып такі ж, і вы не можаце перазапісаць TypeOf() .

Акрамя таго, праграміст можа бачыць падчас кампіляцыі, які метад GetType ён выклікае.

Таму на ваша пытанне: Гэтая мадэль не можа прадстаўляць рэальную пагрозу, але гэта таксама не лепшы стыль кадавання.

2
дададзена
Ды вы маеце рацыю, гэта было выяўлена дрэнна
дададзена аўтар bpoiss, крыніца
<�Код> badFoo.GetType() чаканае паводзіны, таму што GetType быў перагружаны.
дададзена аўтар Wouter Huysentruit, крыніца