วิธีการทดสอบว่า Type เป็นแบบดั้งเดิมหรือไม่


162

ฉันมีบล็อกของรหัสที่ทำให้ประเภทเป็นอนุกรมในแท็ก 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);
}

นี้ทำงานได้ดียกเว้นฉันต้องการให้ทำเช่นนี้เพียงชนิดดั้งเดิมเช่นint, double, boolฯลฯ และประเภทอื่น ๆ ที่ไม่ได้เป็นแบบดั้งเดิม stringแต่สามารถต่อเนื่องได้อย่างง่ายดายเช่น ฉันต้องการให้ละเว้นทุกอย่างอื่นเช่นรายการและประเภทที่กำหนดเองอื่น ๆ

ใครช่วยแนะนำฉันได้ไหม หรือฉันต้องระบุประเภทที่ฉันต้องการอนุญาตให้บางแห่งและเปิดประเภทของคุณสมบัติเพื่อดูว่าได้รับอนุญาตหรือไม่ มันยุ่งนิดหน่อยดังนั้นมันคงจะดีถ้าฉันมีวิธีที่เป็นระเบียบ


12
System.Stringไม่ใช่ประเภทดั้งเดิม
SLAK

3
วิธีที่ดีกว่าที่จะทำคือไม่ใช้ยาสามัญเลย หากคุณสนับสนุนประเภทพารามิเตอร์จำนวนเล็กน้อยตามประเภทของกฏหมายคุณก็จะต้องใช้งานโอเวอร์โหลดจำนวนมาก ถ้าคุณสนับสนุนชนิดใด ๆ ที่ใช้ ISerializable ให้เขียนวิธีการที่ไม่ใช่แบบทั่วไปที่ใช้ ISerializable ใช้ยาชื่อสามัญสำหรับสิ่งที่เป็นจริงทั่วไป ; หากประเภทมีความสำคัญจริง ๆ มันอาจไม่ใช่แบบทั่วไป
Eric Lippert

@Eric: ขอบคุณฉันยังสงสัยว่าคุณสามารถใช้เกณฑ์เดียวกันกับตัวเลขได้หรือไม่ ตัวอย่างเช่นการเขียนฟังก์ชั่นทางคณิตศาสตร์ที่รองรับประเภทตัวเลขทั้งหมดเช่นค่าเฉลี่ยผลรวมและอื่น ๆ พวกเขาควรจะดำเนินการโดยใช้ Generic หรือเกิน? มันสำคัญหรือไม่ว่าการใช้งานจะเหมือนกันหรือไม่? เพราะมันเป็นการดำเนินการแบบเดียวกันกับค่าเฉลี่ยผลรวมสำหรับประเภทตัวเลขใด ๆ ใช่ไหม
Joan Venge

1
@Joan: ความสามารถในการเขียนวิธีคำนวณทางคณิตศาสตร์แบบทั่วไปเกี่ยวกับประเภทที่มีข้อ จำกัด ในการใช้ตัวดำเนินการต่าง ๆ เป็นคุณสมบัติที่ร้องขอบ่อยครั้ง แต่ต้องการการสนับสนุน CLR และมีความซับซ้อนอย่างน่าประหลาดใจ เรากำลังพิจารณาเพื่อใช้ภาษาในอนาคต แต่ไม่มีสัญญา
Eric Lippert

คำตอบ:


184

คุณสามารถใช้สถานที่Type.IsPrimitiveแต่ต้องระวังเพราะมีบางชนิดที่เราสามารถคิดว่าเป็นพื้นฐาน แต่พวกเขาทางจะยกตัวอย่างและDecimalString

แก้ไข 1: เพิ่มรหัสตัวอย่าง

นี่คือตัวอย่างรหัส:

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

แก้ไข 2:ในฐานะที่เป็นความเห็นของ@SLaksมีประเภทอื่น ๆ ที่บางทีคุณอาจต้องการที่จะถือว่าเป็นสิ่งพื้นฐาน ฉันคิดว่าคุณจะต้องเพิ่มรูปแบบนี้ทีละรายการ

แก้ไข 3: IsPrimitive = (บูลีน, ไบต์, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, และ Single), Anther Primitive-like type เพื่อตรวจสอบ (t == typeof (DateTime) ))


12
และบางทีอาจจะDateTime, และTimeSpan DateTimeOffset
Slaks

อืมใช่แล้วคุณพูดถูก ฉันคิดว่าเราจะต้องเพิ่มความเป็นไปได้มากกว่านี้
Javier

2
คุณต้องใช้ลอจิคัลหรือ ( ||) ไม่ใช่ค่าบิตหรือ ( |)
SLAK

42
นี่เป็นวิธีขยายผมเขียนเพื่อความสะดวกในการเรียกใช้การทดสอบที่อธิบายไว้ในคำตอบโดย @Javier และไมเคิล Petito: gist.github.com/3330614
Jonathan

5
คุณสามารถใช้คุณสมบัติType.IsValueTypeและเพิ่มเฉพาะการตรวจสอบสตริง
Matteo Migliore

57

ฉันเพิ่งพบคำถามนี้ขณะที่กำลังมองหาวิธีการแก้ปัญหาที่คล้ายกันและคิดว่าคุณอาจจะสนใจในวิธีการต่อไปนี้โดยใช้และSystem.TypeCodeSystem.Convert

มันง่ายต่อการทำให้เป็นอนุกรมประเภทใด ๆ ที่ถูกแมปกับSystem.TypeCodeอื่น ๆ กว่าSystem.TypeCode.Objectดังนั้นคุณสามารถทำ:

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

ข้อดีของวิธีนี้คือคุณไม่จำเป็นต้องตั้งชื่อประเภทอื่น ๆ ที่ไม่ใช่แบบดั้งเดิมที่ยอมรับได้ คุณสามารถปรับเปลี่ยนรหัสข้างต้นเล็กน้อยเพื่อจัดการประเภทใดก็ได้ที่ใช้ IConvertible


2
นี่เป็นสิ่งที่ยอดเยี่ยมฉันต้องเพิ่มด้วยตนเองGuidเพื่อจุดประสงค์ของฉัน (เป็นแบบดั้งเดิมในคำจำกัดความของฉัน)
Erik Philips

56

เราทำเช่นนี้ใน ORM ของเรา:

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

ฉันรู้ว่าการใช้IsValueTypeไม่ใช่ตัวเลือกที่ดีที่สุด (คุณสามารถมีโครงสร้างที่ซับซ้อนมากของคุณเอง) แต่ใช้งานได้ใน 99% เคส (และรวมถึง Nullables)


6
ทำไมคุณต้องมี IsPrimitive ถ้าคุณใช้ IsValueType? ไม่ใช่ประเภทค่าดั้งเดิมทั้งหมดหรือไม่
JoelFan

5
@JoelFan ประเภททศนิยมมี IsPrimitive false แต่ IsValueType จริง
xhafan

3
@xhafan: คุณตอบคำถามผิด structs ทั้งหมดเป็นเช่นdecimalนั้น แต่มีประเภทที่IsPrimitiveผลตอบแทนtrueแต่IsValueTypeกลับfalse? หากไม่มีประเภทดังกล่าวการt.IsPrimitiveทดสอบนั้นไม่จำเป็น
Lii

6
@Lii คุณพูดถูกทุกประเภทดั้งเดิมIsValueTypeตั้งค่าเป็นจริงดังนั้นการตรวจสอบIsPrimitiveจึงไม่จำเป็น ไชโย!
xhafan

1
@Veverke พวกเขาทำไม่ได้ คุณสามารถมีประเภทค่าที่ไม่ใช่ดั้งเดิมซึ่งในกรณีนี้คุณสมบัติมีค่าที่แตกต่างกัน
Michael Petito

38

จาก @Ronnie Overby การตอบกลับและความคิดเห็น @jonathanconway ฉันได้เขียนวิธีนี้ที่ใช้งานได้สำหรับ Nullable และไม่รวมโครงสร้างของผู้ใช้

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

ด้วย TestCase ต่อไปนี้:

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
นี้เป็นวิธีการที่ดี แต่Enumไม่สนับสนุนให้ทดสอบด้วยและการใช้enum MyEnum { EnumValue } MyEnum@ Jonathan ยังใช้type.IsValueTypeอยู่ กับว่าEnumsมีการตรวจพบอย่างถูกต้อง Structsแต่ยัง ดังนั้นระวังสิ่งที่คุณต้องการดั้งเดิม
Apfelkuacha

1
@Apfelkuacha: คุณพูดถูก แต่แทนที่จะใช้type.IsValueTypeทำไมไม่เพิ่มtype.IsEnum?
Xav987

คุณพูดถูก type.IsEnumเป็นไปได้ ฉันขอแนะนำให้แก้ไขในโพสต์ของคุณ :)
Apfelkuacha

16

นี่เป็นวิธีที่ฉันทำ

   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 มีเหตุผลใดที่คุณใช้IsAssignableFromในการทดสอบแทนที่จะเป็นแบบทดสอบ?
johnny 5

6

ยังเป็นไปได้ที่ดี:

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

ตัวอย่างของทุกคนTypeมีคุณสมบัติที่เรียกว่าIsPrimitive คุณควรใช้สิ่งนั้นแทน
Renan

3
ทั้งStringมิได้Decimalเป็นพื้นฐาน
k3flo

สิ่งนี้ใช้งานได้สำหรับฉัน แต่ฉันเปลี่ยนชื่อเป็น IsClrType เพื่อไม่ให้สับสนกับความหมายของมันกับ. IsPrimitive บนคลาส Type
KnarfaLingus

1
สิ่งนี้จะไม่เลือก Guid หรือ TimeSpan
Stanislav

3

สมมติว่าคุณมีลายเซ็นฟังก์ชันเช่นนี้:

void foo<T>() 

คุณสามารถเพิ่มข้อ จำกัด ทั่วไปเพื่ออนุญาตประเภทค่าเท่านั้น:

void foo<T>() where T : struct

ขอให้สังเกตว่าสิ่งนี้ช่วยให้ไม่เพียง แต่ชนิดดั้งเดิมสำหรับ T แต่ประเภทค่าใด ๆ


2

ฉันมีความต้องการที่จะทำให้เป็นอนุกรมประเภทเพื่อวัตถุประสงค์ในการส่งออกไปยัง XML เมื่อต้องการทำสิ่งนี้ฉันทำซ้ำผ่านวัตถุและเลือกเขตข้อมูลที่เป็นแบบดั้งเดิม enum ชนิดของค่าหรือเป็นอนุกรม นี่คือผลลัพธ์ของแบบสอบถามของฉัน:

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

ฉันใช้ LINQ เพื่อย้ำผ่านประเภทจากนั้นรับชื่อและค่าของพวกเขาเพื่อเก็บไว้ในตารางสัญลักษณ์ ที่สำคัญอยู่ในประโยค 'ที่' ที่ฉันเลือกสำหรับการสะท้อน ฉันเลือกประเภทค่าดั้งเดิมและประเภทที่สามารถเรียงลำดับได้ สิ่งนี้อนุญาตให้วัตถุสตริงและ DateTime ผ่านมาอย่างที่ฉันคาดไว้

ไชโย!


1

นี่คือสิ่งที่ฉันมีในห้องสมุดของฉัน ความคิดเห็นยินดีต้อนรับ

ฉันตรวจสอบ IsValueType ก่อนเพราะมันจัดการกับประเภทส่วนใหญ่แล้วก็ String เพราะมันเป็นอันดับที่สองที่พบบ่อยที่สุด ฉันไม่สามารถนึกถึงสิ่งดั้งเดิมที่ไม่ใช่ประเภทค่าได้ดังนั้นฉันจึงไม่ทราบว่าสิ่งที่ได้รับความนิยม

  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

จากนั้นฉันสามารถใช้มันเช่นนี้:

  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

ฉันแค่ต้องการแบ่งปันโซลูชันของฉัน อาจเป็นประโยชน์กับทุกคน

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 เนื่องจากวัตถุที่ไม่ระบุชื่อไม่มีกำหนดเนมสเปซ


0

นี่เป็นอีกทางเลือกที่ทำงานได้

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.