Tip İlkel Olup Olmadığı Nasıl Test Edilir


162

Html etiketine bir türü serileştiren kod bloğu var.

Type t = typeof(T); // I pass <T> in as a paramter, where myObj is of type T
tagBuilder.Attributes.Add("class", t.Name);
foreach (PropertyInfo prop in t.GetProperties())
{
    object propValue = prop.GetValue(myObj, null);
    string stringValue = propValue != null ? propValue.ToString() : String.Empty;
    tagBuilder.Attributes.Add(prop.Name, stringValue);
}

Ben sadece ilkel türleri, benzeri için bunu yapmak istiyor hariç Bu, büyük işler int, double, boolilkel olmayan ancak benzeri kolayca serileştirilebilir vb ve diğer türlerdeki string. Listeler ve diğer özel türler gibi diğer her şeyi yoksaymasını istiyorum.

Bunu nasıl yaptığımı kimse önerebilir mi? Veya bir yere izin vermek istediğim türleri belirtmem ve izin verilip verilmediğini görmek için mülkün türünü açmam gerekir mi? Bu biraz dağınık, bu yüzden daha düzenli bir yol olsaydı iyi olurdu.


12
System.Stringilkel bir tür değildir.
SLaks

3
Bunu yapmanın en iyi yolu jenerik ilaçları hiç kullanmamaktır. Yasal parametre türleri olarak az sayıda türü destekliyorsanız, bu kadar aşırı yüklemeye sahip olmanız yeterlidir. ISerializable uygulayan herhangi bir türü destekliyorsanız, ISerializable alan genel olmayan bir yöntem yazın. Aslında genel olan şeyler için jenerikler kullanın ; tür gerçekten önemliyse, muhtemelen genel değildir.
Eric Lippert

@Eric: Teşekkürler, aynı ölçütleri sayısal olarak kullanıp kullanamayacağınızı merak ediyorum. Örneğin, Ortalama, Toplam, vb. Gibi tüm sayısal türleri destekleyen matematiksel işlevler yazmak. Bunlar Genel veya aşırı yükler kullanılarak mı uygulanmalıdır? Uygulamanın aynı olup olmadığı önemli mi? Ortalama, herhangi bir sayısal tip için Sum ile hemen hemen aynı işlem olduğu için, değil mi?
Joan Venge

1
@Joan: Çeşitli operatörleri uygulamak için kısıtlanmış tipler üzerine genel aritmetik yöntemler yazabilmek sıkça talep edilen bir özelliktir, ancak CLR desteği gerektirir ve şaşırtıcı derecede karmaşıktır. Bunu dilin gelecekteki sürümleri için düşünüyoruz, ancak söz vermiyoruz.
Eric Lippert

Yanıtlar:


184

Mülkü kullanabilirsiniz Type.IsPrimitive, ancak dikkatli olun, çünkü ilkel olduğunu düşünebileceğimiz bazı türler vardır, ancak bunlar örneğin değildir Decimalve String.

Düzenleme 1: Örnek kod eklendi

İşte bir örnek kod:

if (t.IsPrimitive || t == typeof(Decimal) || t == typeof(String) || ... )
{
    // Is Primitive, or Decimal, or String
}

Düzenleme 2: As @SLaks yorumların da belki ilkel olarak tedavi etmek isteyen diğer türleri vardır. Bu varyasyonları tek tek eklemeniz gerekeceğini düşünüyorum .

Düzenleme 3: IsPrimitive = (Boole, Bayt, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Çift ve Tek), Anther Primitive-Like türü kontrol etmek için (t == typeof (DateTime ))


12
Ve belki DateTime, TimeSpanve DateTimeOffset.
SLaks

Mmmm ... evet, haklısın. Sanırım biraz daha olasılık eklememiz gerekecek
Javier

2
Mantıksal veya ( ||), bitsel veya ( |) kullanmamalısınız.
SLaks

42
@Javier ve Michael Petito'nun cevaplarında açıklanan testleri uygun bir şekilde çalıştırmak için yazdığım bir uzantı yöntemi: gist.github.com/3330614 .
Jonathan

5
Type.IsValueType özelliğini kullanabilir ve yalnızca dizenin denetimini ekleyebilirsiniz.
Matteo Migliore

57

Sadece benzer bir çözüm arayan iken bu soruyu buldum ve kullanmakta şu yaklaşımı ilginizi düşündüm System.TypeCodeve System.Convert.

Bir eşleştirilmiş herhangi türü seri kolaydır System.TypeCodedışındaki System.TypeCode.ObjectYapabileceğin böylece:

object PropertyValue = ...
if(Convert.GetTypeCode(PropertyValue) != TypeCode.Object)
{
    string StringValue = Convert.ToString(PropertyValue);
    ...
}

Bu yaklaşımın avantajı, kabul edilebilir ilkel olmayan diğer tüm isimleri adlandırmanız gerekmemesidir. Ayrıca IConvertible uygulayan herhangi bir türü işlemek için yukarıdaki kodu biraz değiştirebilirsiniz.


2
Bu harika, manuel Guidolarak kendi amaçlarıma eklemek zorunda kaldım (tanımımda ilkel olarak).
Erik Philips

56

ORM'imizde böyle yapıyoruz:

Type t;
bool isPrimitiveType = t.IsPrimitive || t.IsValueType || (t == typeof(string));

Kullanmanın IsValueTypeen iyi seçenek olmadığını biliyorum (kendi çok karmaşık yapılarınız olabilir) ancak% 99 vakada çalışır (ve Nullables'i içerir).


6
IsValueType kullanıyorsanız neden IsPrimitive'a ihtiyacınız var? Tüm ilkel değer türleri değil mi?
JoelFan

5
@JoelFan ondalık türü IsPrimitive false, ancak IsValueType true
xhafan

3
@xhafan: Yanlış soruya cevap veriyorsunuz. Tüm yapılar decimalbu açıdan benzer. Ancak IsPrimitivegeri dönen trueancak IsValueTypegeri dönen bir tür var falsemı? Böyle bir tip yoksa, t.IsPrimitivetest gereksizdir.
Lii

6
@Lii haklısın, her ilkel tip IsValueTypedoğru olarak ayarlandı, bu yüzden kontrol etmek IsPrimitivegerekmez. Şerefe!
xhafan

1
@Veverke Yapmazlar. İlkel olmayan bir değer türüne sahip olabilirsiniz, bu durumda özelliklerin farklı değerleri vardır.
Michael Petito

38

@Ronnie Overby yanıtı ve @jonathanconway yorumundan, Nullable için çalışan ve kullanıcı yapılarını hariç tutan bu yöntemi yazdım.

public static bool IsSimpleType(Type type)
{
    return
        type.IsPrimitive ||
        new Type[] {
            typeof(string),
            typeof(decimal),
            typeof(DateTime),
            typeof(DateTimeOffset),
            typeof(TimeSpan),
            typeof(Guid)
        }.Contains(type) ||
        type.IsEnum ||
        Convert.GetTypeCode(type) != TypeCode.Object ||
        (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSimpleType(type.GetGenericArguments()[0]))
        ;
}

Aşağıdaki TestCase ile:

struct TestStruct
{
    public string Prop1;
    public int Prop2;
}

class TestClass1
{
    public string Prop1;
    public int Prop2;
}

enum TestEnum { TheValue }

[Test]
public void Test1()
{
    Assert.IsTrue(IsSimpleType(typeof(TestEnum)));
    Assert.IsTrue(IsSimpleType(typeof(string)));
    Assert.IsTrue(IsSimpleType(typeof(char)));
    Assert.IsTrue(IsSimpleType(typeof(Guid)));

    Assert.IsTrue(IsSimpleType(typeof(bool)));
    Assert.IsTrue(IsSimpleType(typeof(byte)));
    Assert.IsTrue(IsSimpleType(typeof(short)));
    Assert.IsTrue(IsSimpleType(typeof(int)));
    Assert.IsTrue(IsSimpleType(typeof(long)));
    Assert.IsTrue(IsSimpleType(typeof(float)));
    Assert.IsTrue(IsSimpleType(typeof(double)));
    Assert.IsTrue(IsSimpleType(typeof(decimal)));

    Assert.IsTrue(IsSimpleType(typeof(sbyte)));
    Assert.IsTrue(IsSimpleType(typeof(ushort)));
    Assert.IsTrue(IsSimpleType(typeof(uint)));
    Assert.IsTrue(IsSimpleType(typeof(ulong)));

    Assert.IsTrue(IsSimpleType(typeof(DateTime)));
    Assert.IsTrue(IsSimpleType(typeof(DateTimeOffset)));
    Assert.IsTrue(IsSimpleType(typeof(TimeSpan)));

    Assert.IsFalse(IsSimpleType(typeof(TestStruct)));
    Assert.IsFalse(IsSimpleType(typeof(TestClass1)));

    Assert.IsTrue(IsSimpleType(typeof(TestEnum?)));
    Assert.IsTrue(IsSimpleType(typeof(char?)));
    Assert.IsTrue(IsSimpleType(typeof(Guid?)));

    Assert.IsTrue(IsSimpleType(typeof(bool?)));
    Assert.IsTrue(IsSimpleType(typeof(byte?)));
    Assert.IsTrue(IsSimpleType(typeof(short?)));
    Assert.IsTrue(IsSimpleType(typeof(int?)));
    Assert.IsTrue(IsSimpleType(typeof(long?)));
    Assert.IsTrue(IsSimpleType(typeof(float?)));
    Assert.IsTrue(IsSimpleType(typeof(double?)));
    Assert.IsTrue(IsSimpleType(typeof(decimal?)));

    Assert.IsTrue(IsSimpleType(typeof(sbyte?)));
    Assert.IsTrue(IsSimpleType(typeof(ushort?)));
    Assert.IsTrue(IsSimpleType(typeof(uint?)));
    Assert.IsTrue(IsSimpleType(typeof(ulong?)));

    Assert.IsTrue(IsSimpleType(typeof(DateTime?)));
    Assert.IsTrue(IsSimpleType(typeof(DateTimeOffset?)));
    Assert.IsTrue(IsSimpleType(typeof(TimeSpan?)));

    Assert.IsFalse(IsSimpleType(typeof(TestStruct?)));
}

1
Bu iyi bir yaklaşımdır, ancak Enumdesteklenmez, test edin enum MyEnum { EnumValue }ve kullanın MyEnum. @Jonathan da kullanıyor type.IsValueType. Bununla Enumsdoğru bir şekilde tespit edilir, aynı zamanda Structs. Öyleyse hangi ilkelleri istediğinize dikkat edin.
Apfelkuacha

1
@Apfelkuacha: Tamamen haklısın. Ama kullanmak yerine type.IsValueType, neden eklemiyorsunuz type.IsEnum?
Xav987

tamamen haklısın. type.IsEnumayrıca mümkündür. Yayınınızda bir düzenleme
öneriyorum

16

İşte böyle yaptım.

   static class PrimitiveTypes
   {
       public static readonly Type[] List;

       static PrimitiveTypes()
       {
           var types = new[]
                          {
                              typeof (Enum),
                              typeof (String),
                              typeof (Char),
                              typeof (Guid),

                              typeof (Boolean),
                              typeof (Byte),
                              typeof (Int16),
                              typeof (Int32),
                              typeof (Int64),
                              typeof (Single),
                              typeof (Double),
                              typeof (Decimal),

                              typeof (SByte),
                              typeof (UInt16),
                              typeof (UInt32),
                              typeof (UInt64),

                              typeof (DateTime),
                              typeof (DateTimeOffset),
                              typeof (TimeSpan),
                          };


           var nullTypes = from t in types
                           where t.IsValueType
                           select typeof (Nullable<>).MakeGenericType(t);

           List = types.Concat(nullTypes).ToArray();
       }

       public static bool Test(Type type)
       {
           if (List.Any(x => x.IsAssignableFrom(type)))
               return true;

           var nut = Nullable.GetUnderlyingType(type);
           return nut != null && nut.IsEnum;
       }
   }

@RonnieOverby. IsAssignableFromtestinizde içerir yerine patiküler bir neden var mı?
johnny 5

6

Ayrıca iyi bir olasılık:

private static bool IsPrimitiveType(Type type)
{
    return (type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object);
}

Her örneğinin IsPrimitiveType adlı bir özelliği vardır . Bunun yerine bunu kullanmalısınız.
Renan

3
Ne Stringde Decimalilkel bulunmaktadır.
k3flo

Benim için bu çalışır, ancak ben Tip sınıfı mevcut .IsPrimitive ile anlamını karıştırmayın için IsClrType olarak değiştirildi
KnarfaLingus

1
Örneğin, Guid veya TimeSpan seçmez.
Stanislav

3

Bunun gibi bir işlev imzanız olduğunu varsayarsak:

void foo<T>() 

Yalnızca değer türlerine izin vermek için genel bir kısıtlama ekleyebilirsiniz:

void foo<T>() where T : struct

Bunun sadece T için ilkel tiplere değil, herhangi bir değer tipine de izin verdiğine dikkat edin.


2

Türleri XML'ye dışa aktarma amacıyla serileştirmeye ihtiyacım vardı. Bunu yapmak için, nesne üzerinden yineledim ve ilkel, numaralandırma, değer türleri veya serileştirilebilir alanlar seçtim. Bu benim sorgu sonucu oldu:

Type contextType = context.GetType();

var props = (from property in contextType.GetProperties()
                         let name = property.Name
                         let type = property.PropertyType
                         let value = property.GetValue(context,
                                     (BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.Public),
                                     null, null, null)
                         where (type.IsPrimitive || type.IsEnum || type.IsValueType || type.IsSerializable)
                         select new { Name = name, Value = value});

Türleri yinelemek için LINQ kullandım, sonra bir sembol tablosunda saklamak için adlarını ve değerlerini aldım. Anahtar, düşünmek için seçtiğim 'nerede' maddesinde. İlkel, numaralandırılmış, değer türlerini ve serileştirilebilir türleri seçtim. Bu, dizelerin ve DateTime nesnelerinin beklediğim gibi gelmesine izin verdi.

Şerefe!


1

Kütüphanemde bu var. Yorumlarınızı bekliyoruz.

En yaygın ikinci türü olduğundan, çoğu türü, sonra String işlediğinden önce IsValueType'ı kontrol ederim. Bir değer türü olmayan bir ilkel düşünemiyorum, bu yüzden eğer o bacağının isabet edip etmediğini bilmiyorum.

  Public Shared Function IsPersistable(Type As System.Type) As Boolean
    With TypeInformation.UnderlyingType(Type)
      Return .IsValueType OrElse Type = GetType(String) OrElse .IsPrimitive
    End With
  End Function

  Public Shared Function IsNullable(ByVal Type As System.Type) As Boolean
    Return (Type.IsGenericType) AndAlso (Type.GetGenericTypeDefinition() Is GetType(Nullable(Of )))
  End Function

  Public Shared Function UnderlyingType(ByVal Type As System.Type) As System.Type
    If IsNullable(Type) Then
      Return Nullable.GetUnderlyingType(Type)
    Else
      Return Type
    End If
  End Function

Sonra bu şekilde kullanabilirsiniz:

  Public Shared Function PersistableProperties(Item As System.Type) As IEnumerable(Of System.Reflection.PropertyInfo)
    Return From PropertyInfo In Item.GetProperties()
                     Where PropertyInfo.CanWrite AndAlso (IsPersistable(PropertyInfo.PropertyType))
                     Select PropertyInfo
  End Function

0

Sadece çözümümü paylaşmak istiyorum. Belki de herkes için yararlıdır.

public static bool IsPrimitiveType(Type fieldType)
{
   return fieldType.IsPrimitive || fieldType.Namespace.Equals("System");
}

5
IsPrimitiveType(typeof(System.AccessViolationException)) == true
Ronnie Overby

2
namespace System { class MyNonPrimitiveType { } }
Ronnie Overby

0
public static bool IsPrimitiveType(object myObject)
{
   var myType = myObject.GetType();
   return myType.IsPrimitive || myType.Namespace == null ||  myType.Namespace.Equals("System");
}

NULL ad alanını kontrol etmeyi unutmayın, çünkü anonim nesnelere ad alanı atanmamıştır


0

İşte başka bir geçerli seçenek.

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
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.