Repository: dotnet/runtime
File: src/libraries/System.Reflection.MetadataLoadContext/tests/src/Tests/Type/TypeInvariants.cs (C#)

Code before:

                Type roundTrip2 = module.GetType(fullName, throwOnError: true, ignoreCase: false);
                Assert.Same(type, roundTrip2);
            }

            Assert.Equal<Type>(type.GetInterfaces(), type.GetTypeInfo().ImplementedInterfaces);

            TestUtils.AssertNewObjectReturnedEachTime(() => type.GenericTypeArguments);
            TestUtils.AssertNewObjectReturnedEachTime(() => type.GetTypeInfo().GenericTypeParameters);
            TestUtils.AssertNewObjectReturnedEachTime(() => type.GetGenericArguments());
            TestUtils.AssertNewObjectReturnedEachTime(() => type.GetInterfaces());
            TestUtils.AssertNewObjectReturnedEachTime(() => type.GetTypeInfo().ImplementedInterfaces);
            CustomAttributeTests.ValidateCustomAttributesAllocatesFreshObjectsEachTime(() => type.CustomAttributes);

            // Verify that Type[] returning methods return actual Type[] arrays, not internal implementation types (RoType[], etc.)
            // This prevents ArrayTypeMismatchException when using Span APIs
            Assert.Equal(typeof(Type[]), type.GenericTypeArguments.GetType());
            Assert.Equal(typeof(Type[]), type.GetTypeInfo().GenericTypeParameters.GetType());
            Assert.Equal(typeof(Type[]), type.GetGenericArguments().GetType());
            Assert.Equal(typeof(Type[]), type.GetInterfaces().GetType());
            if (type.IsFunctionPointer)
            {
                Assert.Equal(typeof(Type[]), type.GetFunctionPointerParameterTypes().GetType());
                Assert.Equal(typeof(Type[]), type.GetFunctionPointerCallingConventions().GetType());
            }

            const BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy;
            foreach (MemberInfo mem in type.GetMember("*", MemberTypes.All, bf))
            {
                // Workaround: Do not try to test Array.Initialize, since one of its locals is a function pointer.
                // Delete this workaround when https://github.com/dotnet/runtime/issues/69273 is addressed.
                if (mem.DeclaringType == mem.DeclaringType.Assembly.GetType("System.Array") && mem.Name == "Initialize")
                {
                    continue;
                }

                string s = mem.ToString();
                Assert.Equal(type, mem.ReflectedType);
                Type declaringType = mem.DeclaringType;
                Assert.True(type == declaringType || type.IsSubclassOf(declaringType));

                if (mem is MethodBase methodBase)
                    methodBase.TestMethodBaseInvariants();

                if (type.Assembly.ReflectionOnly)
                {
                    ICustomAttributeProvider icp = mem;
                    Assert.Throws<InvalidOperationException>(() => icp.IsDefined(null, inherit: false));
                    Assert.Throws<InvalidOperationException>(() => icp.GetCustomAttributes(null, inherit: false));
                    Assert.Throws<InvalidOperationException>(() => icp.GetCustomAttributes(inherit: false));


Review comment (bug):
These new assertions use Type.IsFunctionPointer / GetFunctionPointer* APIs, but this test project multi-targets .NET Framework as well as .NETCoreApp. .NET Framework reference assemblies don't define these APIs, so this will fail to compile for the netfx target. Please wrap the function-pointer-specific checks in `#if NET` (or otherwise conditionally compile for .NETCoreApp only).
```suggestion
            Assert.Equal(typeof(Type[]), type.GetInterfaces().GetType());
#if NET
            if (type.IsFunctionPointer)
            {
                Assert.Equal(typeof(Type[]), type.GetFunctionPointerParameterTypes().GetType());
                Assert.Equal(typeof(Type[]), type.GetFunctionPointerCallingConventions().GetType());
            }
#endif
```

Code after:

                Type roundTrip2 = module.GetType(fullName, throwOnError: true, ignoreCase: false);
                Assert.Same(type, roundTrip2);
            }

            Assert.Equal<Type>(type.GetInterfaces(), type.GetTypeInfo().ImplementedInterfaces);

            TestUtils.AssertNewObjectReturnedEachTime(() => type.GenericTypeArguments);
            TestUtils.AssertNewObjectReturnedEachTime(() => type.GetTypeInfo().GenericTypeParameters);
            TestUtils.AssertNewObjectReturnedEachTime(() => type.GetGenericArguments());
            TestUtils.AssertNewObjectReturnedEachTime(() => type.GetInterfaces());
            TestUtils.AssertNewObjectReturnedEachTime(() => type.GetTypeInfo().ImplementedInterfaces);
            CustomAttributeTests.ValidateCustomAttributesAllocatesFreshObjectsEachTime(() => type.CustomAttributes);

            // Verify that Type[] returning methods return actual Type[] arrays, not internal implementation types (RoType[], etc.)
            // This prevents ArrayTypeMismatchException when using Span APIs
            Assert.Equal(typeof(Type[]), type.GenericTypeArguments.GetType());
            Assert.Equal(typeof(Type[]), type.GetTypeInfo().GenericTypeParameters.GetType());
            Assert.Equal(typeof(Type[]), type.GetGenericArguments().GetType());
            Assert.Equal(typeof(Type[]), type.GetInterfaces().GetType());
#if NET
            if (type.IsFunctionPointer)
            {
                Assert.Equal(typeof(Type[]), type.GetFunctionPointerParameterTypes().GetType());
                Assert.Equal(typeof(Type[]), type.GetFunctionPointerCallingConventions().GetType());
            }
#endif

            const BindingFlags bf = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy;
            foreach (MemberInfo mem in type.GetMember("*", MemberTypes.All, bf))
            {
                // Workaround: Do not try to test Array.Initialize, since one of its locals is a function pointer.
                // Delete this workaround when https://github.com/dotnet/runtime/issues/69273 is addressed.
                if (mem.DeclaringType == mem.DeclaringType.Assembly.GetType("System.Array") && mem.Name == "Initialize")
                {
                    continue;
                }

                string s = mem.ToString();
                Assert.Equal(type, mem.ReflectedType);
                Type declaringType = mem.DeclaringType;
                Assert.True(type == declaringType || type.IsSubclassOf(declaringType));

                if (mem is MethodBase methodBase)
                    methodBase.TestMethodBaseInvariants();

                if (type.Assembly.ReflectionOnly)
                {
                    ICustomAttributeProvider icp = mem;
                    Assert.Throws<InvalidOperationException>(() => icp.IsDefined(null, inherit: false));
                    Assert.Throws<InvalidOperationException>(() => icp.GetCustomAttributes(null, inherit: false));