Question

I am working on a method that is using reflection to inspect parameter types of methods. This methods iterates through the ParameterInfo's and is doing something with the types of those parameters.

I was always under the assumption that if TypeInfo.IsClass is true, that this type is a class and always derives (indirectly) from type object (except when the type is object itself of course). So, if TypeInfo.IsClass is true, TypeInfo.BaseType must be set.

Well my assumption was wrong! There are classes that do NOT derive from type object. And my assumption messed up my code.

For example:

Type type = typeof(int).MakeByRefType();

type.IsClass will be true and type.BaseType will be null.

If you think about it, it is logical. And I can prevent my code to crash by checking TypeInfo.IsByRef.

Now my question is: are there more such 'exotic' types (besided the ByRef-types and type object) which are a class (IsClass == true) but do not have a base type (BaseType == null)?

Before you answer: I am only refering to types where IsClass == true! And my example with type int was just an example. It could have been any type. So please no:

  • Interfaces
  • Structs
  • Void

Answers so far:

  • ByRef types (T&): as descrybed in the question.
  • Pointer types (T*): Found by Mark Gravell.
Was it helpful?

Solution

I would say that IsClass is simply misleading here. It states:

Gets a value indicating whether the System.Type is a class; that is, not a value type or interface.

and it is implemented this way: it checks whether the flags include Interface, and whether it is a ValueType.

Unfortunately, there are more things that this. Pointers are not managed types. A by-ref is very similar to a pointer. Pointer are not objects, although in common use the cast is actually a de-reference/cast. The same applies for things like direct pointers such as int*.

Not everything in .NET is an object :)

var baseType = typeof(int*).BaseType; // null
bool liesAndMoreLies = typeof(int*).IsClass; // true

Eric Lippert covers this more here: Not everything derives from object - and lists a few other examples (open generic types, for example).

OTHER TIPS

Except generic types which are not instantiated, is pointed out with the link Not everything derives from object @ Mr. Lippert's blog by Mr. Gravell's good answer, I suggest that to find the other types met your requirement can be done by yourself.

Now, let's start from scratch to solve this question. First off, the types your are going to figure out, should be in the core runtime library, and which is mscorlib.dll:

public static partial class MartinMulderExtensions {
    public static IEnumerable<Type> GetMscorlibTypes() {
        return
            from assembly in AppDomain.CurrentDomain.GetAssemblies()
            let name=assembly.ManifestModule.Name
            where 0==String.Compare("mscorlib.dll", name, true)
            from type in assembly.GetTypes()
            select type;
    }
}

And then, the types have the MakeXXXXType() methods such as MakeByRefType(). Here we take more possibility into account, that is any method which returns a type or types. Since we have zero knowledge about the arguments for an arbitrary type, we consider methods take zero argument:

partial class MartinMulderExtensions {
    public static IEnumerable<Type> GetRetrievableTypes(this Type type) {
        var typesArray=(
            from method in type.GetMethods()
            where 0==method.GetParameters().Count()

            let typeArray=
                method.InvokeZeroArgumentMethodWhichReturnsTypeOrTypes(type)

            where null!=typeArray
            select typeArray).ToArray();

        var types=
            typesArray.Length>0
                ?typesArray.Aggregate(Enumerable.Union)
                :Type.EmptyTypes;

        return types.Union(new[] { type });
    }
}

For the implementation of InvokeZeroArgumentMethodWhichReturnsTypeOrTypes, however, there are several invalid cases of this kind of invocation, such as invoking GetGenericParameterConstraints() on a non-generic type; we are avoiding these cases with try-catch:

partial class MartinMulderExtensions {
    public static IEnumerable<Type>
        InvokeZeroArgumentMethodWhichReturnsTypeOrTypes(
        this MethodInfo method, Type t
        ) {
        try {
            if(typeof(Type)==method.ReturnType) {
                var type=method.Invoke(t, null) as Type;

                if(null!=type)
                    return new[] { type };
            }

            if(typeof(Type[])==method.ReturnType) {
                var types=method.Invoke(t, null) as Type[];

                if(types.Length>0)
                    return types;
            }
        }
        catch(InvalidOperationException) {
        }
        catch(TargetInvocationException) {
        }
        catch(TargetException) {
        }

        return Type.EmptyTypes;
    }
}

And now, to figure out the desired types. Let's construct the method step by step. The first step would be define the scope of all possible types:

partial class MartinMulderExtensions {
    public static Type[] GetDesiredTypes() {
        return (
            from type in MartinMulderExtensions.GetMscorlibTypes()
            .Select(x => x.GetRetrievableTypes())
            .Aggregate(Enumerable.Union)

Then, according to what you stated basically:

Now my question is: are there more such 'exotic' types (besided the ByRef-types and type object) which are a class (IsClass == true) but do not have a base type (BaseType == null)?

            where null==type.BaseType
            where type.IsClass

You also stated that for before answer:

Before you answer: I am only refering to types where IsClass == true! And my example with type int was just an example. It could have been any type. So please no:

  • Interfaces
  • Structs
  • Void
            where !type.IsInterface
            where !type.IsValueType
            where typeof(void)!=type

The final step, let's skip which are already answered and complete the method:

            where !type.IsByRef
            where !type.IsPointer
            select type
            ).ToArray();
    }
}

So now, you can invoke MartinMulderExtensions.GetDesiredTypes() to get the types you desired:

public partial class TestClass {
    public static void TestMethod() {
        foreach(var type in MartinMulderExtensions.GetDesiredTypes())
            Console.WriteLine(type);
    }
}

For the complete code:

public static partial class MartinMulderExtensions {
    public static IEnumerable<Type> GetMscorlibTypes() {
        return
            from assembly in AppDomain.CurrentDomain.GetAssemblies()
            let name=assembly.ManifestModule.Name
            where 0==String.Compare("mscorlib.dll", name, true)
            from type in assembly.GetTypes()
            select type;
    }

    public static IEnumerable<Type>
        InvokeZeroArgumentMethodWhichReturnsTypeOrTypes(
        this MethodInfo method, Type t
        ) {
        try {
            if(typeof(Type)==method.ReturnType) {
                var type=method.Invoke(t, null) as Type;

                if(null!=type)
                    return new[] { type };
            }

            if(typeof(Type[])==method.ReturnType) {
                var types=method.Invoke(t, null) as Type[];

                if(types.Length>0)
                    return types;
            }
        }
        catch(InvalidOperationException) {
        }
        catch(TargetInvocationException) {
        }
        catch(TargetException) {
        }

        return Type.EmptyTypes;
    }

    public static IEnumerable<Type> GetRetrievableTypes(this Type type) {
        var typesArray=(
            from method in type.GetMethods()
            where 0==method.GetParameters().Count()

            let typeArray=
                method.InvokeZeroArgumentMethodWhichReturnsTypeOrTypes(type)

            where null!=typeArray
            select typeArray).ToArray();

        var types=
            typesArray.Length>0
                ?typesArray.Aggregate(Enumerable.Union)
                :Type.EmptyTypes;

        return types.Union(new[] { type });
    }

    public static Type[] GetDesiredTypes() {
        return (
            from type in MartinMulderExtensions.GetMscorlibTypes()
            .Select(x => x.GetRetrievableTypes())
            .Aggregate(Enumerable.Union)

            where null==type.BaseType
            where type.IsClass
            where !type.IsInterface
            where !type.IsValueType
            where typeof(void)!=type

            where !type.IsByRef
            where !type.IsPointer

            select type
            ).ToArray();
    }
}

Type.GetElementType Method (from MSDN)

The Type of the object encompassed or referred to by the current array, pointer, or reference type, or null if the current Type is not an array or a pointer, or is not passed by reference, or represents a generic type or a type parameter in the definition of a generic type or generic method.

Code...

Type type = typeof(int).MakeByRefType();
bool isClass = type.IsClass; // true

Type elementType = type.GetElementType(); // Int32
Type baseType = elementType.BaseType; // ValueType
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top