Làm thế nào để kiểm tra nếu loại là nguyên thủy


162

Tôi có một khối mã nối tiếp một loại vào thẻ 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);
}

Đây hoạt động tuyệt vời, ngoại trừ tôi muốn nó chỉ làm điều này với nhiều loại nguyên thủy, giống như int, double, boolvv, và các loại khác mà không phải là nguyên thủy, nhưng có thể được tuần tự một cách dễ dàng như string. Tôi muốn nó bỏ qua mọi thứ khác như Danh sách & các loại tùy chỉnh khác.

Bất cứ ai có thể đề nghị làm thế nào tôi làm điều này? Hoặc tôi cần chỉ định loại tôi muốn cho phép ở đâu đó và bật loại thuộc tính để xem có được phép không? Đó là một chút lộn xộn, vì vậy sẽ tốt hơn nếu tôi có một cách gọn gàng hơn.


12
System.Stringkhông phải là một loại nguyên thủy.
SLaks

3
Cách tốt hơn để làm điều đó là không sử dụng thuốc generic. Nếu bạn hỗ trợ một số lượng nhỏ các loại như các loại tham số hợp pháp thì đơn giản là có quá nhiều loại. Nếu bạn hỗ trợ bất kỳ loại nào thực hiện ISerializable, thì hãy viết một phương thức không chung chung để lấy ISerializable. Sử dụng thuốc generic cho những thứ thực sự chung chung ; nếu loại thực sự quan trọng, nó có thể không chung chung.
Eric Lippert

@Eric: Cảm ơn, tôi cũng đang tự hỏi liệu bạn có thể sử dụng các tiêu chí tương tự với số không? Chẳng hạn, để viết các hàm toán học hỗ trợ tất cả các loại số, tức là Trung bình, Tổng, v.v. Chúng nên được thực hiện bằng cách sử dụng Chung hoặc quá tải? Có vấn đề gì cho dù việc thực hiện có giống nhau hay không? Bởi vì đó là hoạt động khá giống nhau đối với Trung bình, Tổng cho bất kỳ loại số nào, phải không?
Joan Venge

1
@Joan: Có thể viết các phương pháp số học chung về các loại bị ràng buộc để thực hiện các toán tử khác nhau là một tính năng được yêu cầu thường xuyên, nhưng nó yêu cầu hỗ trợ CLR và rất phức tạp. Chúng tôi đang xem xét nó cho các phiên bản tương lai của ngôn ngữ, nhưng không hứa hẹn.
Eric Lippert

Câu trả lời:


182

Bạn có thể sử dụng tài sản Type.IsPrimitive, nhưng hãy cẩn thận vì có một số loại mà chúng ta có thể nghĩ là nguyên thủy, nhưng chúng không phải là ví dụ DecimalString.

Chỉnh sửa 1: Đã thêm mã mẫu

Đây là một mã mẫu:

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

Chỉnh sửa 2: Theo nhận xét của @SLaks , có những loại khác mà có thể bạn cũng muốn coi là nguyên thủy. Tôi nghĩ rằng vì bạn sẽ phải thêm các biến thể này từng người một .

Chỉnh sửa 3: IsPrimitive = (Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double và Single), loại Anther Primitive-Like để kiểm tra (t == typeof ))


12
Và có lẽ DateTime, TimeSpanDateTimeOffset.
SLaks

Mmmm ... vâng, bạn đúng. Tôi nghĩ rằng chúng tôi sẽ phải bổ sung thêm một số khả năng
Javier

2
Bạn cần sử dụng logic hoặc ( ||), không phải bitwise hoặc ( |).
SLaks

42
Đây là một phương pháp mở rộng mà tôi đã viết để thuận tiện chạy các bài kiểm tra được mô tả trong các câu trả lời của @Javier và Michael Petito: gist.github.com/3330614 .
Jonathan

5
Bạn có thể sử dụng thuộc tính Type.IsValueType và chỉ thêm kiểm tra cho chuỗi.
Matteo Migliore

57

Tôi vừa tìm thấy câu hỏi này trong khi tìm kiếm một giải pháp tương tự, và nghĩ rằng bạn có thể quan tâm đến phương pháp sau đây bằng cách sử dụng System.TypeCodeSystem.Convert.

Thật dễ dàng để tuần tự hóa bất kỳ loại nào được ánh xạ sang System.TypeCodeloại khác System.TypeCode.Object, vì vậy bạn có thể làm:

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

Ưu điểm của phương pháp này là bạn không phải đặt tên cho mọi loại không nguyên thủy được chấp nhận khác. Bạn cũng có thể sửa đổi mã ở trên một chút để xử lý bất kỳ loại nào thực hiện IConvertible.


2
Điều này thật tuyệt, tôi đã phải tự thêm Guidvào cho mục đích của riêng mình (như một nguyên thủy trong định nghĩa của tôi).
Erik Philips

56

Chúng tôi làm như thế này trong ORM của chúng tôi:

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

Tôi biết rằng sử dụng IsValueTypekhông phải là lựa chọn tốt nhất (bạn có thể có các cấu trúc rất phức tạp của riêng mình) nhưng nó hoạt động trong 99% trường hợp (và bao gồm cả Nullables).


6
Tại sao bạn cần IsPrimitive nếu bạn đang sử dụng IsValueType? Không phải tất cả các loại giá trị nguyên thủy?
JoelFan

5
Kiểu thập phân @JoelFan có IsPrimitive false, nhưng IsValueType true
xhafan

3
@xhafan: Bạn trả lời sai câu hỏi. Tất cả các cấu trúc là như thế decimaltrong vấn đề đó. Nhưng có một loại mà IsPrimitivetrả về truenhưng IsValueTypetrả về false? Nếu không có loại đó thì t.IsPrimitivekiểm tra là không cần thiết.
Lii

6
@Lii bạn nói đúng, mọi kiểu nguyên thủy đều IsValueTypeđược đặt thành true, vì vậy việc kiểm tra IsPrimitivelà không cần thiết. Chúc mừng!
xhafan

1
@Veverke Họ không. Bạn có thể có một loại giá trị không nguyên thủy, trong trường hợp đó các thuộc tính có các giá trị khác nhau.
Michael Petito

38

Từ phản hồi @Ronnie Overby và bình luận @jonathanconway, tôi đã viết phương pháp này hoạt động cho Nullable và loại trừ các cấu trúc người dùng.

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

Với TestCase sau:

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
Đây là một cách tiếp cận tốt, nhưng Enumkhông được hỗ trợ, hãy thử enum MyEnum { EnumValue }và sử dụng nó MyEnum. @Jonathan cũng đang sử dụng type.IsValueType. Với điều đó Enumsđược phát hiện chính xác, nhưng cũng có Structs. Vì vậy, xem ra những gì nguyên thủy bạn muốn.
Apfelkuacha

1
@Apfelkuacha: bạn hoàn toàn đúng. Nhưng thay vì sử dụng type.IsValueType, tại sao đơn giản là không thêm type.IsEnum?
Xav987

bạn hoàn toàn đúng. type.IsEnumcũng có thể Tôi đã đề nghị chỉnh sửa bài đăng của bạn :)
Apfelkuacha

16

Đây là cách tôi đã làm nó.

   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. Có bất kỳ lý do paticular bạn sử dụng IsAssignableFromtrong bài kiểm tra của bạn thay vì chứa?
ngày 5

6

Cũng là một khả năng tốt:

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

Mỗi trường hợp Typecó một thuộc tính được gọi là IsPrimitive . Bạn nên sử dụng nó thay thế.
Renan

3
Không Stringphải Decimallà nguyên thủy.
k3flo

Điều này hoạt động với tôi, nhưng tôi đã đổi tên thành IsClrType để không nhầm lẫn ý nghĩa của nó với .IsPrimitive trên lớp Type
KnarfaLingus

1
Điều này sẽ không chọn Guid hoặc TimeSpan, ví dụ.
Stanislav

3

Giả sử bạn có chữ ký hàm như thế này:

void foo<T>() 

Bạn có thể thêm một ràng buộc chung để chỉ cho phép các loại giá trị:

void foo<T>() where T : struct

Lưu ý rằng điều này không chỉ cho phép các kiểu nguyên thủy cho T, mà bất kỳ loại giá trị nào.


2

Tôi có nhu cầu tuần tự hóa các loại cho mục đích xuất chúng sang XML. Để làm điều này, tôi đã lặp qua đối tượng và chọn các trường nguyên thủy, enum, các loại giá trị hoặc tuần tự hóa. Đây là kết quả của truy vấn của tôi:

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ôi đã sử dụng LINQ để lặp qua các loại, sau đó lấy tên và giá trị của chúng để lưu trữ trong bảng ký hiệu. Chìa khóa nằm trong mệnh đề 'where' mà tôi đã chọn để phản ánh. Tôi đã chọn các kiểu nguyên thủy, liệt kê, các giá trị và các kiểu tuần tự hóa. Điều này cho phép các chuỗi và các đối tượng DateTime đi qua như tôi mong đợi.

Chúc mừng!


1

Đây là những gì tôi có trong thư viện của tôi. Bình luận được chào đón.

Tôi kiểm tra IsValueType trước, vì nó xử lý hầu hết các loại, sau đó là String, vì đây là loại phổ biến thứ hai. Tôi không thể nghĩ về một nguyên thủy không phải là một loại giá trị, vì vậy tôi không biết liệu chân đó có bao giờ bị tấn công hay không.

  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

Sau đó tôi có thể sử dụng nó như thế này:

  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

Tôi chỉ muốn chia sẻ giải pháp của tôi. Có lẽ nó hữu ích cho bất cứ ai.

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

Đừng quên kiểm tra không gian tên NULL, vì các đối tượng ẩn danh không được gán không gian tên


0

Đây là một lựa chọn khả thi khác.

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.