3

First off, I do have a workaround for this issue (use type.FullName), so its just for interest sake really.

*Edited for clarification; This is really just a question of what is the best way to compare the type of Type in code.

object o;
Type t = typeof(someclass);
o = t;

// Cant do this
if (o.GetType() == typeof(RuntimeType)) {
}

The object being attached to o can be anything, including types. I'm examining the type of the object to see how to deal with it further. So if it was a string, I might do one thing, if its an enum something else, if its a type, something else again. I'm basically dealing with the same sort of thing as String.Format("",x,y,z) where the parameters are all completely arbitrary.

I could write

if (o.GetType().FullName == "System.RuntimeType") {} or
if (o.GetType()== typeof(Type).GetType()) {}

But both are pretty ugly looking (though it works).

Original Question:

Apologies if this has been asked before, but I cant find an exact match (there are a lot of how do I get a type of object, object is class, or object.GetType() style questions.)

This question came close, but its not quite the same since I cant avoid calling GetType on a type(I don't think? Hope I'm not overlooking something really simple or cheaty...); What's the difference between System.Type and System.RuntimeType in C#?

So basically, I've created an attribute with an arbitrary number of parameters. These can be of any object, including types (I use the type to decide how the property the attribute is attached to should be handled). For example, while the property could be an integer, this integer is the primary key in a database for some particular table. If I assign this type to the attribute, I can write generic code to deal with any kind of object without having to write a ton of special case code. That said, I could use a string, or any other value like an enum, but since the models already exist there didnt seem to be any point in not using them since I can just create instances of them with Activator.CreateContext() based on the type being passed in.

[AttributeUsage(AttributeTargets.Property)]
public class SomeAttribute: System.Attribute
{
    public SomeAttribute(params object[] parameters)
    {
        foreach(var p in parameters) {
            ...
            // Type of Type.  Uh oh.
            Type t = p.GetType();

            // Evaluates as false when t is of type Type(its actually RuntimeType, with is protected). Sad face.
            if (t == typeof(Type)) {
                ...
            }
            ...
        }
    }
}

And I've slapped this attribute on some properties;

public class someclass
{
    [SomeAttribute(..., typeof(someotherclass))
    public sometype someproperty { get; set; }
}

When the program gets to

if (t == typeof(Type))

if always return false. The type of t is evaluated as System.RuntimeType as opposed to System.Type. Unfortunately I cant just change that to

if (t == typeof(RuntimeType))

Since I get a compiler error of "'RuntimeType' is inaccessible due to its protection level".

Is there any way to perform a type match on RuntimeType other than by looking at the type.Name or type.FullName?

*reedited for more clarification.

  • Can you give us more context to the use case of your code? It's hard to say why your comparisons are always returning false with the information you've given us. – Austin Brunkhorst Dec 18 '17 at 03:34
  • seems like `if (p is Type) {` – Slai Dec 18 '17 at 04:16
  • Its for auditing changes made to objects. Any property in an object with the attribute get compared to the previous state, and if they differ, an entry is made in an audit log, recording what the change was and who made it. The code above should compile without issue if you remove the ...'s. Well, unless I've made a typo somewhere. The gist of the question was really "t.Name == "RuntimeType" is ugly (as is t == typeof(Type).GetType(). – There and That Dec 18 '17 at 04:21
  • if (p is Type) { ... } might be it. Using it is causing the application to throw me a runtime error somewhere else in the code at the moment. I'll chase that down and see whats up. – There and That Dec 18 '17 at 04:37
  • Guess I'm having all the brainfarts today. The reason I cant use if (p is Type) is because not only does this match the type Type, it also matches all other types e.g. it matches typeof(string). Thats why I was getting the runtime error. – There and That Dec 18 '17 at 06:00
  • Please explain what you want to accomplish instead of what you've tried. Why are you comparing it to `Type` at all? – Lasse V. Karlsen Dec 18 '17 at 06:29
  • Please post the problem you're trying to solve, not the solutions that you're having problems implementing for it. – Lasse V. Karlsen Dec 18 '17 at 06:31
  • Yes. I do have two solutions. But they're kind of ugly; 1. use t.FullName == "System.RuntimeType", or 2. use t == typeof(Type).GetType() The reason why I'm comparing types is because the attribute an take artibitrary information. E.g. [SomeAttribute("somestring", 123, typeof(someclass))]. – There and That Dec 18 '17 at 06:35

3 Answers3

2

Consider Type is a normal class.

Type t1 = typeof(string);
Type t2 = "1".GetType();

There's no difference between t1 and t2. And What is t1? Just consider t1 or t2 as a normal object and the type of the object is System.Type. It means that t1 or t2 nearly equals new Type("System.String").

How can I know an object obj is aStringBuilder?

Just use is or as:

bool objIsStringBuilder = obj is StringBuilder;

How can I know t1 is a Type?

In the same way:

bool t1IsType = t1 is Type;

What is typeof(Type) or t1.GetType()?

Type t3 = typeof(Type);

Then t3 is nearly equals new Type("System.Type"). So of course t1 and t3 are not equal.

skyoxZ
  • 362
  • 1
  • 3
  • 10
  • Sorry, if I understand correctly, you're trying to explain why the above doesn't work? I realise that Type t is not the same as t.GetType(). I guess I haven't worded it obviously enough? But the real reason I initially posted is that its not possible to match using t == typeof(RuntimeType) because its protected. – There and That Dec 18 '17 at 05:54
  • @ThereandThat Why you want to use `RuntimeType`? I think that `Type t = p as Type;` and check if t == null is enough. – skyoxZ Dec 18 '17 at 06:43
  • p as Type appears to match any kind of object, I guess since all objects have an associated type. Just like p is Type. It will never be null :( – There and That Dec 18 '17 at 07:03
  • @ThereandThat yes all objects have an assoiated type, so the value of `p.GetType()` is always a `Type`, but not the object itself. Test `new StringBuilder() is Type`. – skyoxZ Dec 18 '17 at 07:19
  • yes, new StringBuilder() is type returns null. But the issue is I'm storing the type of uninstantiated classes into an object (object o = typeof(StringBuilder)). I'm then checking the type of the object to see what to do with it. The object may not be a type of Type at all, it could be anything, which is why I need to check what the type of the object is before I can do anything with it. The reason why this is, is that I'm dealing with an attribute that takes arbitrary parameters. Same sort of thing as String.Format("",x,y,z). – There and That Dec 18 '17 at 08:01
  • To clarify a bit further, once I've check have a type I can do this; object instance = Activator.CreateInstance((Type)p); This means I don't have to write checks for each individual kind of type I'm passing in. – There and That Dec 18 '17 at 08:11
  • @ThereandThat Are all parameters of the constructor always `Type`? If so, you should define the constructor as `public SomeAttribute(params Type[] parameters)` – skyoxZ Dec 18 '17 at 08:19
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/161429/discussion-between-skyoxz-and-there-and-that). – skyoxZ Dec 18 '17 at 08:30
0

I think I should have drunk more coffee or slept in this morning. This works though it seems just as wrong as using type.FullName.

if (t == typeof(Type).GetType()) {
}

Oh well.

  • I did state it seemed wrong, but why downvote unless you have a better solution? – There and That Dec 18 '17 at 03:51
  • Does `t.IsAssignableFrom(typeof(Type))` (https://msdn.microsoft.com/en-us/library/system.type.isassignablefrom%28v=vs.110%29.aspx) evaluate to `True`? – Jesse C. Slicer Dec 18 '17 at 03:51
  • No, t.IsAssignableFrom(typeof(Type)) does not work. It also evaluates as false. – There and That Dec 18 '17 at 04:00
  • Apologies, I shouldn't assume. It does look very very wrong. Though it does produce the right result . Type being the type Type, not some arbitrary type. I guess most people just assume (like me, ha.) – There and That Dec 18 '17 at 05:00
0

In order to test whether a variable is a type of Type, use typeof(Type).IsAssignableFrom(x). This expression returns true if x is typeof(Type) or typeof(RuntimeType) but false for other types such as typeof(string).

Type x = typeof(string);
Type y = typeof(Type);
Type z = typeof(Type).GetType(); // RuntimeType

typeof(Type).IsAssignableFrom(x); // false;
typeof(Type).IsAssignableFrom(y); // true;
typeof(Type).IsAssignableFrom(y); // true;

x is Type; // true;
y is Type; // true;
z is Type; // true;

The issue with expression x is Type is that it will return true for all types such as typeof(Type) as well as typeof(string).

How is this useful? Imagine you have to find types of Type without access to the original objects, for examlpe:

object[] objects = {"hello", typeof(string), 5}; 
Type[] types = objects.Select(x => x.GetType()).ToArray();

// Imagine we are given just the `types` array with a task to get
// indices of types of Type.

for (int i = 0; i < types.Length; ++i) {
  if (typeof(Type).IsAssignableFrom(types[i])) {
    Console.WriteLine($"Object at index {i} is Type!");
  }
}
// prints: "Object at index 1 is Type!"

I have also encountered this while doing some reflection on objects and determining whether a value of a field is a type of Type (when the type was just passed to me without access to the original FieldInfo data.

NightElfik
  • 4,328
  • 5
  • 27
  • 34