19

The title says it all, here's the same with some formatting:

Why can't I write

public bool IsHashSet(object obj)
{
    return obj is HashSet<>;
}

but this is okay:

public bool IsHashSet(object obj)
{
    return obj.GetType() == typeof(HashSet<>);
}

(The same goes for all generics and isn't limited to HashSet)

Null
  • 1,950
  • 9
  • 30
  • 33
Rand Random
  • 7,300
  • 10
  • 40
  • 88

2 Answers2

35

Your function

public bool IsHashSet(object obj)
{
  return obj.GetType() == typeof(HashSet<>);
}

will return false for every possible value of obj, except null, in which case it will throw a NullReferenceException. It will not check if obj is a hash set. typeof(HashSet<int>) and typeof(HashSet<>) are two different types.

It is for that same reason that obj is HashSet<> is rejected. It's completely useless. The only difference between the two functions is that one is useless in a way the compiler knows about, and the other is useless in a way the compiler doesn't know about.

You can use type.IsGenericType and type.GetGenericTypeDefinition(), then compare the result of the latter to typeof(HashSet<>). However, you should ask yourself if that is useful: obj is HashSet<int> would also evaluate to true if obj is derived from HashSet<int>. Working with obj.GetType() would require you to check the class hierarchy yourself.

You can write a reusable helper function to check this for other generic types too:

public static bool IsInstanceOfGenericType(object obj, Type genericType) {
  if (obj == null)
    return false;

  var type = obj.GetType();
  while (type != null) {
    if (type.IsGenericType && type.GetGenericTypeDefinition() == genericType)
      return true;

    type = type.BaseType;
  }
  return false;
}

You can call this as IsInstanceOfGenericType(someObject, typeof(HashSet<>)).

To respond to your comments:

In my understanding of HashSet<> would mean HashSet of any generic, so maybe this would work typeof(HashSet<>).IsAssignableFrom(HashSet<int>)

It would not. It's possible you're thinking of Java, which as I understand it does have something like that, but C# does not. HashSet<int> and HashSet<> are related types, but their relation is not one related to inheritance.

if not whats the meaning of HashSet<>

It is the HashSet<T> type before it has got any specific type argument. It can be used to construct the real types, for example after var t = typeof(int);, typeof(HashSet<>).MakeGenericType(t) can be used to get typeof(HashSet<int>). It can be useful if t is not known at compile-time. But outside of such dynamic type construction, it is not meaningful.

and why is it valid to write in a typeof() but not in is HashSet<>?

It's not valid with is HashSet<> because it would never be meaningful. It is impossible to construct any object whose type is HashSet<>.

  • 1
    I dont really get why the typeof(HashSet<>) fails, maybe you could explain it to me in more detail? I dont really care what generic it is and want the simplest possible check if its Something (Do have to really write for all Generics a type static extension that checks the GetGenericTypeDefinition?) – Rand Random Apr 29 '14 at 18:31
  • @RandRandom If `typeof(HashSet) != typeof(HashSet<>)`, and `obj.GetType() == typeof(HashSet)`, then it logically follows that `obj.GetType() == typeof(HashSet<>)` cannot also be true. No, you wouldn't need to write a new method for each type you want to check, you can add that as a parameter. Will edit my answer to include that in a short while. –  Apr 29 '14 at 18:35
  • 1
    In my understanding of HashSet<> would mean HashSet of any generic, so maybe this would work typeof(HashSet<>).IsAssignableFrom(HashSet) if not whats the meaning of HashSet<> and why is it valid to write in a typeof() but not in is HashSet<>? – Rand Random Apr 29 '14 at 18:39
  • It does answer my question to such an extent that I gave you +1 for 4 different answers aswell, especially the `typeof(HashSet<>).MakeGenericType(t)` looks really helpfull, thanks for all the info and the patience with a noob like me. :) – Rand Random Apr 29 '14 at 18:53
  • 4
    Oh, while I appreciate that, upvoting multiple answers only because of who posted them is actually something that is generally not accepted, and can result in action being taken against those votes. Glad to hear it helped, though. –  Apr 29 '14 at 18:58
  • So C# instantiates generics at compile-time just like C++ templates? – Siyuan Ren Apr 30 '14 at 01:52
  • @C.R. If it happened purely at compile-time, then `typeof(HashSet<>).MakeGenericType(typeof(int))` could never work unless the program contained a mention of `HashSet` somewhere already. But if it happened purely at run-time, then `new(HashSet).Add(3)` could not be checked by the compiler. It's somewhere in between, but the compiler pretty much does as little work as necessary. –  Apr 30 '14 at 11:02
  • @hvd: Seems more magic than both Java's generics and C++'s templates. – Siyuan Ren Apr 30 '14 at 12:40
7

Neither actually works, it's just that the first one will fail at compile time. What you probably want is something like this:

public bool IsHashSet(object obj)
{
    if (obj != null) 
    {
        var t = obj.GetType();
        if (t.IsGenericType) {
            return t.GetGenericTypeDefinition() == typeof(HashSet<>);
        }
    }
    return false;
}
Matt Burland
  • 44,552
  • 18
  • 99
  • 171
  • 4
    that doesn't answer the question at all – Selman Genç Apr 29 '14 at 18:11
  • 1
    Not sure why this gets a downvote. It's the same information I had added in my answer at about the same time you posted your answer. It could be improved by checking `obj == null`, and checking `t`'s base classes, but depending on what the OP is after, it may be good enough as it is. –  Apr 29 '14 at 18:13
  • 1
    @Selman22: The question as to why had already been answered. I was suggesting an alternative way to achieve what I presume is the aim of the OP. I assume they weren't asking for purely academic reasons, they are asking because they want to check if an object is a `HashSet<>`. Some times it's worth answering the real question instead of being pedantic. – Matt Burland Apr 29 '14 at 18:13
  • @Selman22 It does (indirectly, but clear enough to me), by pointing out that the OP is comparing the wrong type to `typeof(HashSet<>)`. –  Apr 29 '14 at 18:14
  • @hvd: Thanks. I did notice right after I submitted that you answer had been updated to include the same information. – Matt Burland Apr 29 '14 at 18:15