'Class not inheriting from object?
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.
Solution 1:[1]
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 object
s, 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).
Solution 2:[2]
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 typeint
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();
}
}
Solution 3:[3]
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
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Eluvatar |
Solution 2 | Community |
Solution 3 | user2315985 |