7

I was pondering of this while I was writing some helper functions dealing with reflection. Is there anything else besides classes, structs, enums and interfaces in C#? If I write a function that checks for class, struct, enum and interface, would that be the all encompassing function?

I read that delegates are classes anyway here and here.

Help me make my idea on this kind of hierarchy perfect:

   reference type                   value type
         |                               |    
  ---------------                   ----------
  |             |                   |        |
interface    class                struct    enum


// the all encompassing function - pseudo code:
public static bool IsC#Stuff(this Type type)
{
    return type.IsEnum || type.IsStruct || type.IsClass || type.IsInterface;
}

Am I missing something?

Community
  • 1
  • 1
nawfal
  • 70,104
  • 56
  • 326
  • 368
  • 1
    pointers and several kinds of references(for example the ones used for `ref` parameters) – CodesInChaos May 12 '13 at 19:37
  • @asawyer delegates are anyway classes internally. Confirmed by `typeof(Func<>).IsClass`. But I have no idea why do you say generics. – nawfal May 12 '13 at 19:38
  • 2
    Just a note about your hierarchy: value types can implement interfaces as well as reference types.. – tukaef May 12 '13 at 19:40
  • 1
    generics make an interesting case: the `T` in a generic type/method can be **either** value-typed *or* reference-typed; there are special op-codes ("unbox any" and "constrained") to allow this work – Marc Gravell May 12 '13 at 19:54
  • @MarcGravell but at runtime they inevitably have to be either value type or reference type isn't it? I mean if I'm writing a function to handle all types, it covers generic type parameter, at least at run time isn't it? – nawfal May 13 '13 at 03:41
  • @nawfal for different actual `T` for a closed generic, yes it will be known at execution. However, note that generics can be invoked via reflection/dynamic, meaning a `T` that *didn't exist* a moment ago can be used. – Marc Gravell May 13 '13 at 06:38
  • 1
    @nawfal: Array and delegate *instances* are much like other heap object instances, but array and delegate *type descriptions* (i.e. the instances of `System.Type` that define the array and delegate types) are special because they encapsulate other types. Had generics been part of the NET 1.0 spec, delegates and arrays might have been handled in much the same way as other generic types, but instead there's special handling to make them quasi-generic. – supercat May 13 '13 at 16:26

2 Answers2

3

There are also arrays and delegates, although those are actually classes.

There are also references (ref parameters to methods) and generic type parameters in definitions of generic methods or types.

In unsafe code, there are also pointers.

SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • Hmm, interesting. Never thought about pointers. But I don't get why you mention `ref` parameters and generic type parameters. Are they equivalent to classes, structs etc? – nawfal May 12 '13 at 19:49
  • @nawfal: No; they are not. – SLaks May 12 '13 at 19:54
  • 1
    @nawfal: If one has an `int` field, and one calls `Interlocked.Increment(ref myField)`, the passed parameter is an `Int32 byref`. Normal "reference" types hold standalone persistent object references which may be kept around as long as desired, and will keep the underlying object around as long as the reference exists. Each standalone object has some "extra" information associated with it to help the garbage collector. A "byref" may refer to a primitive which has no GC information stored with it, but will only be valid within the scope of the procedure to which it is passed. – supercat May 13 '13 at 15:32
1

Not sure is this is a direct answer to your question but .NET has 5 types:
class, struct, interface, delegate, and enum

Common Type System

paparazzo
  • 44,497
  • 23
  • 105
  • 176
  • Blam, delegates are basically classes internally. – nawfal May 13 '13 at 03:31
  • 2
    @nawfal: Value types are also classes (they derive from `System.ValueType` which derives from `System.Object`) Sometimes the distinction is useful, sometimes not. Sometimes distinguishing delegates is useful, and sometimes not. – Ben Voigt May 13 '13 at 03:43
  • @BenVoigt that's right, but here I'm talking about the base object itself. I know `int` is `ValueType` which in turn is a class. But `typeof(int).IsClass` returns `false` even though `typeof(ValueType).IsClass` is `true`. Similarly, `typeof(Delegate).IsClass` is `true`, but even `typeof(Func<>).IsClass` returns `true`. At one point that was confusing, but I did some reading on the subject, and realised internally they were classes (with special syntax). Kindly go thru the two links I attached in the question. – nawfal May 13 '13 at 03:47
  • @nawfal: That's because the specification of `IsClass` isn't "true if it is a class". It's "true if it is a class but it is not a value type". – Ben Voigt May 13 '13 at 03:49
  • @BenVoigt is there a reference to that? I can find this in docs: *Gets a value indicating whether the Type is a class; that is, not a value type or interface.* When is a type a class, but a value type too? – nawfal May 13 '13 at 03:55
  • @nawfal: Run `ILDASM` on a value type sometime, and you'll see that internally to .NET, it is a class that inherits `System.ValueType` (or `System.Enum`). – Ben Voigt May 13 '13 at 04:12
  • @BenVoigt oh.. from that point its getting complicated. Then you're right, *sometimes the distinction is pointless* :) But I do think *for a distinction* lets take what .NET decides for granted, after all they are the people who decide better. In that respect, pointers and delegates are `IsClass` :) – nawfal May 13 '13 at 04:16
  • 1
    @nawfal That is the formal type definition from the CTS. – paparazzo May 13 '13 at 13:35
  • @BenVoigt: Each value type has associated with it a kind of storage location and a kind of heap object. Value-type storage locations exhibit value semantics rather than reference semantics, but the corresponding heap objects always exhibit mutable reference semantics. Those semantics are implemented by .NET, rather than by the types themselves. Boxed value-types objects aren't usually mutated, but they can be, even in `safe` code. Such mutation doesn't go through any of the code in the type itself, so there's nothing the type can do to prevent it. – supercat May 13 '13 at 15:28