如何以编程方式检查一个类型是结构体还是类?

74

如何以编程方式检查一个类型是结构体还是类?


1
在C#反射中,如何获取类型的类型? - bdukes
8
如果这是一个重复的内容,则不是来自给定的链接。 - Stefan Steinegger
8个回答

100

使用Type.IsValueType

获取一个值,该值指示类型是否为值类型。

可以像这样使用它:

typeof(Foo).IsValueType

或者在执行时像这样:

fooInstance.GetType().IsValueType
相反地,还有一个Type.IsClass 属性(在我看来应该称为IsReferenceType,但无论如何),根据您要测试的内容,并不一定更合适。代码总是阅读起来没有布尔否定语句更好,所以使用任何一种可以提高代码可读性的方式。


正如Stefan在下面指出的那样,为了正确地识别结构体,必须小心避免在处理枚举时出现误报。枚举是值类型,因此对于枚举和结构体,IsValueType属性将返回true

因此,如果您真的正在寻找structs而不仅仅是通用的值类型,则需要这样做:

Type fooType = fooInstance.GetType();
Boolean isStruct = fooType.IsValueType && !fooType.IsEnum;

26
原文:A primitive type is also a value type.翻译: 原始类型也是值类型。 - Stefan Steinegger
3
@Stephan - 的说法没错,所有的C#基本数据类型都是值类型,但这并不意味着所有值类型都是C#基本数据类型。System.Guid和System.DateTime都是值类型,但它们不是语言原生的基本数据类型。 - Andrew Hare
2
为了进一步阐述我的观点,术语“primitive”是特殊的,实际上只保留给某些已覆盖 System.Type 中的 IsPrimitiveImpl 方法类型。没有任何阻止微软实现一个新的原始引用类型的事情。原始类型并不需要也是值类型。 - Andrew Hare
7
正确的,枚举和结构体是C#支持的两种值类型。一个有用的记忆方式是结构体是一种值类型,而不是反过来。 - Andrew Hare
1
我可以补充一下,在元数据中,原始概念是特殊的,因为实际的元数据定义了这些原始类型的使用方式和解释方式。原始类型在元数据中并不被定义为System.Int32或者System.Double,而是一个具有独特字节的特殊本地类型,用于指定Int32(ELEMENT_TYPE_I4或者0x08)和Double(ELEMENT_TYPE_R8或者0x0D)。我之所以知道这些,是因为我曾经写过一个元数据解析器。 - Allen Clark Copeland Jr
显示剩余14条评论

42
Type type = typeof(Foo);

bool isStruct = type.IsValueType && !type.IsPrimitive;
bool isClass = type.IsClass;
它仍然可以是原始类型或接口。

编辑:有关结构体定义的讨论很多。实际上,结构体和值类型是一样的,因此IsValueType 是正确的答案。我通常需要知道一个类型是否为自定义结构,这意味着使用关键字struct而不是原始类型来实现该类型。因此,我保留我的回答,供所有遇到同样问题的人参考。


编辑2:根据C#参考文档,枚举不是结构体,而任何其他值类型都是。因此,确定类型是否为结构体的正确答案是:

bool isStruct = type.IsValueType && !type.IsEnum;

依我之见,结构体的定义比较令人困惑而不是符合逻辑。实际上,我怀疑这种定义在实践中是否有任何相关性。


原始类型的问题已经在我的回答评论中被讨论得够透彻了 :) 你不必担心接口,因为接口类型实例将对IsClassIsValueType都返回false。此外,任何实现该接口的类型都将返回其真实类型,无论该类型的引用是作为接口还是其他类型。 - Andrew Hare
1
@Stefan:你是在说基本类型不能作为结构体吗?如果是这样,那么你是错误的。例如,C#规范的第11节说:“C#提供的简单类型,如intdoublebool,实际上都是struct类型”。 - LukeH
1
@Luke:是的,这确实是原始类型的定义。我总是需要知道一个类型是否为用户定义的结构体,排除原始类型,通常简称为“结构体”。但你是对的,严格来说,原始类型也是结构体。 - Stefan Steinegger
type.IsValueType && !type.IsPrimitive 无法确定结构体是否是使用关键字 struct 创建的用户定义结构体。例如,它会对 decimalint? 或任何枚举返回错误的结果。 - thepirat000
@thepirat000:是的。在C#中没有“用户定义结构”的概念。十进制数和可空类型与其他任何结构体一样。当然,如果有意义的话,你可以明确地将它们排除在外。这里有一个相关但不同的问题,有一个更好的答案:https://dev59.com/rXRA5IYBdhLWcg3wsgBP#863944 - Stefan Steinegger

4
扩展方法。对于我的代码中定义为结构体的任何内容返回true,但对于不属于我的用途的内容(如int),尽管它们在技术上是结构体,但不会返回true
我需要知道一个类型是否可能具有子字段或属性,但被定义为struct而不是class。因为当您更改struct时,它只更改了副本,然后您必须将原始内容设置回更改后的副本才能使更改“生效”。
public static bool IsStruct(this Type source) 
{
  return source.IsValueType && !source.IsPrimitive && !source.IsEnum;
}

十进制是值类型而不是原始类型(也不是枚举)。 - Panu Oksala

2
我认为应该是这样的:

它是否是一种结构

public bool IsStructure(Type LocalType)
{
    bool result = false;
    if (LocalType.IsValueType)
    {
        //Is Value Type
        if (!LocalType.IsPrimitive)
        {
            /* Is not primitive. Remember that primitives are:
            Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32,
            Int64, UInt64, IntPtr, UIntPtr, Char, Double, Single.
            This way, could still be Decimal, Date or Enum. */
            if (!LocalType == typeof(decimal))
            {
                //Is not Decimal
                if (!LocalType == typeof(DateTime))
                {
                    //Is not Date
                    if (!LocalType.IsEnum)
                    {
                        //Is not Enum. Consequently it is a structure.
                        result = true;
                    }
                }
            }
        }
    }
    return result;
}

这是一个类吗?

public bool IsClass(Type LocalType)
{
    bool result = false;
    if (LocalType.IsClass)
    {
        //Is Class or Delegate
        if (LocalType.GetType != typeof(Delegate))
            result = true;
    }
    return result;
}

1
请提供一些解释。 - Peter Csala

1

请尝试以下操作

bool IsStruct(Type t) {
  return t.IsValueType;
}

3
查看@Stefan的答案:type.IsValueType && !type.IsEnum翻译:判断类型是否为值类型并且不是枚举类型。 - gap

1

这个结构体:

// determine if the type is a struct..
var isStruct = type.IsValueType && !type.IsEnum &&
               !type.IsEquivalentTo(typeof(decimal)) && 
               !type.IsPrimitive;

这个类:

var isClass = type.IsClass;

答案:
var isClassOrStruct = isStruct | isClass;

0
    //I think it would be something like this:

    public sbyte IsStructure(Type LocalType)
    {
            sbyte result = false;
            if (LocalType.IsValueType)
            {
                    //Is Value Type
                    if (!LocalType.IsPrimitive)
                    {
                            /* Is not primitive. Remember that primitives are:
                            Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32,
                            Int64, UInt64, IntPtr, UIntPtr, Char, Double, Single.
                            This way, could still be Decimal, Date or Enum. */
                            if (!LocalType == typeof(decimal))
                            {
                                    //Is not Decimal
                                    if (!LocalType == typeof(DateTime))
                                    {
                                            //Is not Date
                                            if (!LocalType.IsEnum)
                                            {
                                                    //Is not Enum. Consequently it is a structure.
                                                    result = true;
                                            }
                                    }
                            }
                    }
            }
            return result;
    }

-1
对于每种值类型,都有一个相应的自动生成的类类型,它派生自System.ValueType,而后者又派生自System.Object。请注意,值类型本身不派生自任何东西,但可以隐式转换为该类类型,并且该类类型的实例可以显式转换为值类型。
考虑以下代码:
        public static int GetSomething<T>(T enumerator) where T : IEnumerator<int>
        {
            T enumerator2 = enumerator;
            enumerator.MoveNext();
            enumerator2.MoveNext();
            return enumerator2.Current;
        }
在类型为List<int>.Enumerator的变量上调用此例程将产生与在类型为IEnumerator<int>的变量上调用它时非常不同的行为,后者恰好存储了List<int>.Enumerator的实例。即使类型为List<int>.Enumerator的变量是值类型,在类型为IEnumerator<int>的变量中存储的List<int>.Enumerator实例将表现为类类型。

3
这怎么回答问题了? - nawfal

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接