Cara Menguji apakah Jenis Primitif

162

Saya memiliki blok kode yang membuat serial suatu jenis menjadi tag Html.

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

Ini karya besar, kecuali saya ingin hanya melakukan ini untuk tipe primitif, seperti int, double, booldll, dan jenis lainnya yang tidak primitif tapi dapat serial dengan mudah seperti string. Saya ingin mengabaikan hal lain seperti Daftar & jenis kustom lainnya.

Adakah yang bisa menyarankan bagaimana saya melakukan ini? Atau apakah saya perlu menentukan tipe yang ingin saya izinkan di suatu tempat dan mengaktifkan tipe properti untuk melihat apakah itu diperbolehkan? Itu agak berantakan, jadi alangkah baiknya jika saya ada cara yang lebih rapi.

DaveDev
sumber
12
System.Stringbukan tipe primitif.
SLaks
3
Cara yang lebih baik untuk melakukannya adalah tidak menggunakan obat generik sama sekali. Jika Anda mendukung sejumlah kecil jenis sebagai tipe parameter hukum, maka cukup banyak kelebihan. Jika Anda mendukung semua jenis yang mengimplementasikan ISerializable, maka tulis metode non-generik yang menggunakan ISerializable. Gunakan obat generik untuk hal-hal yang sebenarnya generik ; jika jenisnya benar-benar penting, mungkin tidak generik.
Eric Lippert
@ Eric: Terima kasih, saya juga ingin tahu apakah Anda dapat menggunakan kriteria yang sama dengan angka? Misalnya untuk menulis fungsi matematika yang mendukung semua jenis numerik, yaitu Rata-rata, Jumlah, dll. Haruskah mereka diimplementasikan menggunakan Generik atau kelebihan muatan? Apakah masalah apakah implementasinya sama atau tidak? Karena ini operasi yang hampir sama untuk Average, Sum untuk semua jenis numerik, kan?
Joan Venge
1
@ Joan: Mampu menulis metode aritmatika generik pada jenis yang dibatasi untuk mengimplementasikan berbagai operator adalah fitur yang sering diminta, tetapi membutuhkan dukungan CLR dan sangat rumit. Kami sedang mempertimbangkannya untuk versi bahasa yang akan datang, tetapi tidak ada janji.
Eric Lippert

Jawaban:

182

Anda dapat menggunakan properti Type.IsPrimitive, tetapi berhati-hatilah karena ada beberapa tipe yang dapat kita anggap primitif, tetapi mereka tidak, misalnya Decimaldan String.

Sunting 1: Menambahkan kode sampel

Berikut ini contoh kode:

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

Sunting 2: Seperti komentar @SLaks , ada jenis lain yang mungkin ingin Anda perlakukan sebagai primitif. Saya pikir Anda harus menambahkan variasi ini satu per satu .

Sunting 3: IsPrimitive = (Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, dan Single), jenis Anther Primitive-like untuk memeriksa (t == typeof (DateTime) ))

Javier
sumber
12
Dan mungkin DateTime, TimeSpandan DateTimeOffset.
SLaks
Mmmm ... ya, Anda benar. Saya pikir kita harus menambahkan beberapa kemungkinan
Javier
2
Anda perlu menggunakan logika atau ( ||), bukan bitwise atau ( |).
SLaks
42
Inilah metode ekstensi yang saya tulis untuk menjalankan tes yang dijelaskan dalam jawaban oleh @Javier dan Michael Petito dengan mudah: gist.github.com/3330614 .
Jonathan
5
Anda bisa menggunakan properti Type.IsValueType dan hanya menambahkan tanda centang untuk string.
Matteo Migliore
57

Saya baru saja menemukan pertanyaan ini sambil mencari solusi yang serupa, dan berpikir Anda mungkin tertarik pada pendekatan berikut menggunakan System.TypeCodedan System.Convert.

Sangat mudah untuk membuat serial apa pun jenis yang dipetakan ke System.TypeCodeselain System.TypeCode.Object, jadi Anda bisa melakukan:

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

Keuntungan dari pendekatan ini adalah Anda tidak perlu menyebutkan setiap tipe non-primitif lainnya yang dapat diterima. Anda juga dapat memodifikasi kode di atas sedikit untuk menangani semua jenis yang mengimplementasikan IConvertible.

Michael Petito
sumber
2
Ini bagus, saya harus menambahkan secara manual Guiduntuk keperluan saya sendiri (sebagai primitif dalam definisi saya).
Erik Philips
56

Kami melakukannya seperti ini di ORM kami:

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

Saya tahu bahwa menggunakan IsValueTypebukanlah pilihan terbaik (Anda dapat memiliki struct Anda sendiri yang sangat kompleks) tetapi berfungsi dalam 99% kasus (dan termasuk Nullables).

Alex
sumber
6
Mengapa Anda perlu IsPrimitive jika Anda menggunakan IsValueType? Tidak semua tipe nilai primitif?
JoelFan
5
@JoelFan tipe desimal memiliki IsPrimitive false, tetapi IsValueType true
xhafan
3
@xhafan: Anda menjawab pertanyaan yang salah. Semua struct seperti decimaldalam hal itu. Tetapi apakah ada tipe yang IsPrimitivepengembalian truetetapi IsValueTypepengembalian false? Jika tidak ada tipe seperti itu maka t.IsPrimitivetes tidak perlu.
Lii
6
@ Lii Anda benar, setiap tipe primitif telah IsValueTypedisetel ke true, jadi memeriksa IsPrimitivetidak diperlukan. Bersulang!
xhafan
1
@ Veverke Mereka tidak. Anda dapat memiliki tipe nilai non-primitif, dalam hal ini properti memiliki nilai yang berbeda.
Michael Petito
38

Dari tanggapan @Ronnie Overby dan komentar @jonathanconway, saya menulis metode ini yang berfungsi untuk Nullable, dan mengecualikan struct pengguna.

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

Dengan TestCase berikut:

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?)));
}
Xav987
sumber
1
Ini adalah pendekatan yang baik, tetapi Enumtidak didukung, uji dengan enum MyEnum { EnumValue }dan gunakan MyEnum. @ Jonathan juga menggunakan type.IsValueType. Dengan itu Enumsterdeteksi dengan benar, tetapi juga Structs. Jadi hati-hati dengan primitif apa yang Anda inginkan.
Apfelkuacha
1
@Apfelkuacha: Anda sepenuhnya benar. Namun alih-alih menggunakan type.IsValueType, mengapa tidak menambahkan type.IsEnum?
Xav987
kamu benar sekali. type.IsEnumitu juga mungkin. Saya telah menyarankan edit pada posting Anda :)
Apfelkuacha
16

Begini cara saya melakukannya.

   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;
       }
   }
Ronnie Overby
sumber
@RonnieOverby. Adakah alasan paticular yang Anda gunakan IsAssignableFromdalam pengujian Anda alih-alih mengandung?
johnny 5
6

Juga kemungkinan yang baik:

private static bool IsPrimitiveType(Type type)
{
    return (type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object);
}
k3flo
sumber
Setiap instance dari Typememiliki properti yang disebut IsPrimitive . Anda harus menggunakannya sebagai gantinya.
Renan
3
Baik Stringatau Decimalyang primitif.
k3flo
Ini berfungsi untuk saya, tetapi saya mengganti nama menjadi IsClrType agar tidak membingungkan maknanya dengan yang ada .IsPrimitive pada kelas Type
KnarfaLingus
1
Ini tidak akan memilih Guid atau TimeSpan, misalnya.
Stanislav
3

Dengan asumsi Anda memiliki tanda tangan fungsi seperti ini:

void foo<T>() 

Anda bisa menambahkan batasan umum untuk mengizinkan hanya tipe nilai:

void foo<T>() where T : struct

Perhatikan bahwa ini memungkinkan tidak hanya tipe primitif untuk T, tetapi semua tipe nilai.

eWolf
sumber
2

Saya memiliki kebutuhan untuk membuat cerita bersambung jenis untuk tujuan mengekspornya ke XML. Untuk melakukan ini, saya mengulangi melalui objek dan memilih bidang yang primitif, enum, tipe nilai atau serializable. Ini adalah hasil dari permintaan saya:

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

Saya menggunakan LINQ untuk beralih melalui tipe, kemudian mendapatkan nama dan nilai mereka untuk disimpan dalam tabel simbol. Kuncinya ada pada klausa 'di mana' yang saya pilih untuk refleksi. Saya memilih tipe nilai primitif, enumerasi, dan tipe serial. Ini memungkinkan untuk string dan objek DateTime untuk datang seperti yang saya harapkan.

Bersulang!

JFalcon
sumber
1

Ini yang saya miliki di perpustakaan saya. Komentar diterima.

Saya memeriksa IsValueType terlebih dahulu, karena menangani sebagian besar jenis, lalu String, karena ini yang paling umum kedua. Saya tidak bisa memikirkan primitif yang bukan tipe nilai, jadi saya tidak tahu apakah kaki itu pernah terkena.

  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

Maka saya bisa menggunakannya seperti ini:

  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
toddmo
sumber
0

Saya hanya ingin membagikan solusi saya. Mungkin bermanfaat bagi siapa pun.

public static bool IsPrimitiveType(Type fieldType)
{
   return fieldType.IsPrimitive || fieldType.Namespace.Equals("System");
}
Bahamut
sumber
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");
}

Jangan lupa untuk memeriksa NULL namespace, karena objek anonim tidak memiliki namespace yang ditetapkan

iDusko
sumber
0

Berikut adalah opsi lain yang layak.

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
pengguna2023116
sumber