Soru C # / .NET'de bir değerin kutulu olup olmadığı nasıl test edilir?


Bir değerin kutulu olup olmadığını sınayan bir kod yazmanın bir yolunu arıyorum.

Ön araştırmalarım, .NET'in gerçeği gizlemek için yolunun dışına çıktığını gösteriyor. GetType() ve IsValueType kutulu bir değer ile kutusuz bir değer arasındaki farkı gösterme. Örneğin, aşağıdaki LinqPad C # ifadelerinde, buna inancım var o1 kutulu ve i1 kutulu değildir, ancak kodda test etmenin bir yolunu ya da ikinci en iyi şekilde bilmenin bir yolunu KESİNLİKLE herhangi bir değişkene veya değere bakarken, kutulu veya kutulu değilse, türü "dinamik" veya "nesne" olsa bile.

Herhangi bir tavsiye?

// boxed? -- no way to tell from these answers!
object o1 = 123;
o1.GetType().Dump("o1.GetType()");
o1.GetType().IsValueType.Dump("o1.GetType().IsValueType");

// not boxed? -- no way to tell from these answers!
int i1 = 123;
i1.GetType().Dump("i1.GetType()");
i1.GetType().IsValueType.Dump("i1.GetType().IsValueType");

25
2018-04-27 15:49


Menşei


Neden senin için önemli olduğunu merak ediyorum? 'Kutulu' veya 'kutusuz' nesnelere nasıl farklı davranacaksınız? - Cos Callis
@Cos: Boks performans etkileri vardır. - Robert Harvey♦
@Jodrell - önerilerinizi belki de bazı kodları çizerek detaylandırır mısınız? - Reb.Cabin
@Robert, @Rep ... iyi bir cevabınız var gibi görünüyor olsa da, hala "neden" sorusu ile ilgileniyorum. Evet, performans sonuçları var, ancak bu tasarım zamanı kararıdır. Bir nesne, çalışma zamanında veya kutuda değilse nasıl farklı şekilde ele alınacaktır? Şunun gibi bir şey kullanmayı planlıyor musunuz: if(oThing.isBoxed) {//do this;} else {//do that;}?? - Cos Callis
@Cos: Amaç Assert() Arayan tarafından gönderilen değer kutsanırsa. Kullanım durumu, yüksek performanslı bir kitaplıktır, böylece kutulu değerler kullanıldığında performans düşer. - Robert Harvey♦


Cevaplar:


Takip etmeyi dene

public static bool IsBoxed<T>(T value)
{
    return 
        (typeof(T).IsInterface || typeof(T) == typeof(object)) &&
        value != null &&
        value.GetType().IsValueType;
}

Bir jenerik kullanarak, fonksiyonun derleyici tarafından görüntülenen ifadenin türünü ve bunun temelini hesaba katmasına izin veriyoruz.

Console.WriteLine(IsBoxed(42));  // False
Console.WriteLine(IsBoxed((object)42)); // True
Console.WriteLine(IsBoxed((IComparable)42));  // True

DÜZENLE

Bir kaç kişi bunun neden jenerik olması gerektiğine dair açıklama istedi. Ve bunun bile neden gerekli olduğunu sorguladı, geliştirici kodlara bakıp bir değerin kutulu olup olmadığını söyleyemez mi? Her iki soruyu da yanıtlama girişiminde, aşağıdaki yöntem imzasını dikkate alınız.

void Example<T>(T param1, object param2, ISomething param3) where T : ISomething {
  object local1 = param1;
  ISomething local2 = param1;
  ...
}

Bu senaryoda, sağlanan parametrelerden veya yerlilerin herhangi biri potansiyel olarak kutulu değerleri temsil edebilir ve kolayca olamaz. Rahat muayene ile anlatmak imkansızdır, sadece çalışma zamanı türünün bir kombinasyonunun incelenmesi ve değerin tutulduğu referans bunu belirleyebilir.


32
2018-04-27 16:05



@Allon bir değerin kutulu olup olmadığını bilmek için hem çalışma zamanına hem de zaman bilgisine ihtiyacınız vardır. Bu, iç içe geçmiş jeneriklerle uğraşırken özellikle önemlidir - JaredPar
@ Allon, genel olarak özellikle yararlı olduğunu düşünmüyorum. Ama bu geçerli bir sorudur, bu yüzden en iyi cevabı vermeyi denedim. Ama nasıl bir değer olacağını görebiliyordum. Debug.Assert kontrol et çok performansa duyarlı uygulama, kutulu değerler ile belleği boşa harcamamak için - JaredPar
@ Allon: Bir kitap yazarı olduğumu ve kitaplığımı kullanabilecek tüm kodlara erişemediğimi varsayın. Hata ayıklamalarında, kutu kitaplık değerlerimi kütüphane rutinine geçirerek, kullanıcılara kusursuz bir ceza uyguladıklarını bildirmek isterim. Kütüphanemde statik kontrollerle bunu yapmanın bir yolunu görmüyorum. - Reb.Cabin
BTW, daha iyi bir uygulama olacağını düşünüyorum return !typeof(T).IsValueType && value != null && value.GetType().IsValueType; Nll-referansları ve durumları ele alır. T = System.ValueType. - Ani
@ Rastgele832 aslında sizin kısıtlamaları dikte eğer makul bir duruş. Performans sorunları ve boks performans üzerinde doğrudan bir etkiye sahiptir. - JaredPar


Hadi, hile kullanalım ...

Biz ne biliyoruz?

  • Değer-tipi değişken tekrar ve tekrar kutulu olur referans tipi değişkene atandığında
  • Referans tipi değişken kutulu değil tekrar ...

Bu yüzden tekrar kutulu (başka bir nesneye) alıp alamayacağımızı kontrol edeceğiz ... böylece referansları karşılaştırıyoruz

isReferenceType olacak yanlış Burada, yığın üzerinde 2 nesne karşılaştırdığımızdan (bir kutulu surelyBoxed, yalnızca bir adet ReferenceEquals çağrısında bulunur):

int checkedVariable = 123;     //any type of variable can be used here
object surelyBoxed = checkedVariable;
bool isReferenceType = object.ReferenceEquals(surelyBoxed, checkedVariable);

isReferenceType olacak doğru Burada, 1 nesneyi yığına karşılaştırdığımızda:

object checkedVariable = 123;     //any type of variable can be used here
object surelyBoxed = checkedVariable;
bool isReferenceType = object.ReferenceEquals(surelyBoxed, checkedVariable);

Bu, HERHANGİ tür için çalışır, sadece için değil int ve object

Kullanışlı bir yönteme koymak için:

    public static bool IsReferenceType<T>(T input)
    {
        object surelyBoxed = input;
        return object.ReferenceEquals(surelyBoxed, input);
    }

Bu yöntem şu şekilde kolayca kullanılabilir:

int i1 = 123;
object o1 = 123;
//...
bool i1Referential = IsReferenceType(i1);  //returns false
bool o1Referential = IsReferenceType(o1);  //returns true

6
2018-04-27 16:31



Bence iyi bir temel fikriniz var, ancak bazı hatalar var: ReferenceEquals () - metodu zaten nesneleri alır, bu yüzden eğer T değeri bir değer ise, her iki girdi de "surelyBoxed", ve eğer T değil ise, hiçbiri kutulu değildir. . Yani ihtiyacın olan tek şey public static bool IsReferenceType<T>(T input){ return object.ReferenceEquals(input, input); } (çünkü her argümanı kutlarsa, aynı değişken olsalar bile, ayrı ayrı kutulanır). - AnorZaken
@Anor, sanırım açıklamalarının özünü kaçırıyorsun. ReferenceEquals'ın yolunda kutladıkları gerçek, test etmek için kullandığı şey. ReferenceEquals'a giderken 'surelyBoxed' kelimesini 'manualBoxed' veya 'implicitlyBoxed' olarak düşünün. Ancak, dışarıda iki nesne değişkeni oluşturmuşsa, tam olarak aynı şekilde çalışır, ardından bunları ReferenceEquals'a gönderir. Eğer zaten nesneler olsaydı, aynı nesneye sadece üç referans olurlardı. Değer türleri olsaydı, iki farklı kutulu örnek olurdu. Mantıklı olmak? - MarqueIV
@MarqueIV no, sanırım benim noktayı kaçırdınız: surelyBoxed değişkeni gereksizdir - sonuç üzerinde hiçbir etkisi yoktur. Sadece 1 satır kod kaydedebileceğini ileri sürüyordum. - AnorZaken
@Anor, bunu tartışmak üzereydim, çünkü o olmadan, ReferenceEquals (giriş, giriş) yapıyor olacaksınız, ancak daha sonra, bunun, iki farklı kutuda zaten değer türleri için olacağını söylediniz. her iki parametrede aynı değişkeni geçerken anlam, ReferenceEquals zaten esasen NoBoxNeeded işlevi. Yerdesin. - MarqueIV


Bir değişkenin kutulu bir tam sayı olup olmadığını kontrol etmek için bazı basit yardımcı yöntemler şunlardır:

public static bool IsBoxed(object item)
{
    return true;
}

public static bool IsBoxed<T>(T item) where T : struct
{
    return false;
}

Sadece ara IsBoxed(...) değişkeninizde:

IsBoxed(o1) // evaluates to true
IsBoxed(i1) // evaluates to false

Bu elbette hiçbir şey yapmaz. Bir değerin kutulu olup olmadığını tam olarak niçin bilmeniz gerekiyor?


4
2018-04-27 16:02



kesinlikle ikinci olabilir IsBoxed<T>(T item) where T : struct. - Random832
@JSBangs, bu cevap benim için eşdeğer olmayacak çünkü sadece int için çalışıyor. Düzenlemelerde bile bu yanıt yanlış olarak aşağıdakileri doğru olarak işaretler: IsBoxed("hello world"); - JaredPar
@JaredPar: Yukarıdaki kod ciddi olmamalıdır. Bir değişkenin kutulu olup olmadığını kontrol etmenin yararsızlığını göstermek içindir. Sahip olmakla eşdeğerdir bool IsProgramRunning { return true; } yöntem. Tabii ki sorunun cevabı problemli, aynı şekilde soru. - Allon Guralnek
İlk "IsBoxed" in uygulanmasını anlamıyorum. Onu boş geçirdiğini varsayalım. Doğru döner. Ama null kutulu bir şey değil. "Merhaba" ilettiğini varsayalım. Doğru döner. Ama "merhaba" kutulu bir şey değildir. Eğer amacınız, sorunun kendisinin garip olması ise, sizin dikkatinizi çekerim. Ancak bu yöntemler, reklamını yaptıkları şeyi yapmazlar. - Eric Lippert
Kvb: Hayır, olamazdı. "Kutulu bir nullable" diye bir şey yoktur. Bir nullable int kutulu olduğunda, ya null olur ya da kutulu bir int olur. Asla "kutulu null" haline gelmez. Böyle bir hayvan yok. - Eric Lippert


GetType () ve IsValueType gösterilmiyor   kutulu değer arasındaki fark   ve kutulanmamış bir değer.

GetType kapalı (sanal olmayan) bir yöntemdir System.Object. Bu yöntemin bir değer türünde çağrılması kesinlikle kutu. Hatta Nullable<T> Bu etrafında almak mümkün - çağrı GetType Bir nullable üzerinde bir değeri varsa (altta yatan tip olarak kutulu) veya bir NullReferenceException eğer değilse (null kutuluysa, null-reference değerine sahip olamaz).

bakarken   herhangi bir değişken veya değer,   türü "dinamik" veya "nesne" dir   kutulu veya kutulu değil.

Genel olarak, bir değer türünü "tutan" bir ifadeniz varsa, bu ifadenin değeri bir kutuya referans olur olmadıkça ifadenin derleme zamanı türü, değer türünün kendisidir (jenerikler biraz daha karmaşıktır). Kutulu yapılara referanslar tutabilen ortak referans türleri object, dynamic ve arabirim türleri.


4
2018-04-27 16:01



Bence Intellisense bana bir ifadenin statik tipini ve eğer bir değer tipiyse ve bu yüzden kutulu değilse söyler. İyi. - Reb.Cabin
Generics konusunu biraz detaylandırır mısınız - genel bir türün bir örneğinin statik olarak bir değer türü olup olmadığını nasıl anlatabilirim? Bu, içinde aritmetik işleçleri olan genel bir rutini yazma problemiyle ilgili gibi görünüyor ... Bu imkansız bir şey çünkü derleyicinin kodlar üretemediğinden, operatörlerin intrinsik işleçlerin değer türleri üzerinde olup olmadığını bilmeden (+ ve * int gibi) ) veya tipte yöntemler olan operatör aşırı yükleri olmalıdır. - Reb.Cabin
Son paragrafınız doğru cevaptır, çünkü "jenerik" kullanıldığında "derleme zamanı" türü çalışma zamanına kadar belirlenemeyebilir. - supercat


Benzer Allon'un cevabıancak derleme zamanı hatası oluşturmadan herhangi bir tür için doğru cevabı döndürmelidir:

int i = 123;
Console.WriteLine(IsBoxed(i));    // false

object o = 123;
Console.WriteLine(IsBoxed(o));    // true

IComparable c = 123;
Console.WriteLine(IsBoxed(c));    // true

ValueType v = 123;
Console.WriteLine(IsBoxed(v));    // true

int? n1 = 123;
Console.WriteLine(IsBoxed(n1));    // false
int? n2 = null;
Console.WriteLine(IsBoxed(n2));    // false

string s1 = "foo";
Console.WriteLine(IsBoxed(s1));    // false
string s2 = null;
Console.WriteLine(IsBoxed(s2));    // false

// ...

public static bool IsBoxed<T>(T item)
{
    return (item != null) && (default(T) == null) && item.GetType().IsValueType;
}

public static bool IsBoxed<T>(T? item) where T : struct
{
    return false;
}

(Ancak, Allon'un kodunun neden olduğu olası derleme zamanı hatasının bir hata değil, bir hata olduğunu söyleyebilmenize rağmen: bir derleme zamanı hatası verdiyseniz, kesinlikle kutulanmamış bir değer türü ile uğraşmazsınız!)


2
2018-04-28 14:03





Bir tür bir değer türüyse ve statik türü 'dinamik' veya 'nesne' veya bir arabirimse, her zaman kutulu.

Bir tür bir değer türü ve statik türü ise gerçek tipse, asla kutulu.


2
2018-04-27 16:01



"Her zaman kutulu" kategorisine arayüzler ekleyin. - supercat


Bence soru aslında yanlış bir şey. Asıl soru şu, “Bir nesnenin başka bir tür için bir kutu olup olmadığını nasıl anlarım?”

Allon'un yorumuna referansla, Object türünde bir nesneniz varsa ve nesne ilkel bir değer tipiyse, bir kutu. Bunun% 100 doğru olduğundan emin değilim, fakat (Allon'un uygulamasına benzer):

// Assume there is some object o.
bool isBoxed = o.GetType().IsPrimitive;

1
2018-04-27 17:29



çok yararlı. [T> public void testi kullanıyordum (bu T obj) {bool isPrimitive = typeof (T) .IsPrimitive; } ancak sonuçlar kutulu bir ilkelde yanlıştı - Devin Garner


Bu yaklaşım Jared Par'ın cevabına benzer. Ama bence !typeof(T).IsValueTypekutulu bir değer içerebilecek tüm türleri sıralamaktan daha temizdir.

public static bool IsBoxed<T>(T value)
{
    return !typeof(T).IsValueType && (value != null) && value.GetType().IsValueType;
}

Jared'in kodundan farklı olarak bu durum T olduğu System.ValueType doğru şekilde.

Başka bir ince nokta şu ki value.GetType().IsValueType sonra gelir !typeof(T).IsValueType aksi halde GetType() değerin geçici kutulu bir kopyasını oluşturur.


1
2017-08-21 20:39





Bunun kimseyle ilgili olup olmayacağından emin değilim, ancak bu yazıyla karşılaştığımdan beri, boks aslında çok dinamik haritalandırmamı etkiliyordu.

Sigil fantastik bir şey UnBoxAny yöntemi

Aşağıdakileri varsayarsak:

public class Report { public decimal Total { get; set; } }

new Dictionary<string, object> { { "Total", 5m} }

Yani ondalık değer kutulu.

var totalProperty = typeof(Report).GetProperty("Total");

var value = emit.DeclareLocal<object>();

//invoke TryGetValue on dictionary to populate local 'value'*
                                                    //stack: [bool returned-TryGetValue]
//either Pop() or use in If/Else to consume value **
                                                    //stack: 
//load the Report instance to the top of the stack 
//(or create a new Report)
                                                    //stack: [report]
emit.LoadLocal(value);                              //stack: [report] [object value]
emit.UnboxAny(totalProperty.PropertyType);          //stack: [report] [decimal value]

//setter has signature "void (this Report, decimal)" 
//so it consumes two values off the stack and pushes nothing

emit.CallVirtual(totalProperty.SetMethod);          //stack: 

* TryGetValue'yi çağır

** If / Else'de kullan


1
2018-04-28 19:35