Osy#betaRun it on Osyrin: osyrin.com

Reference / Types

typeof and GetType() — a Type is its name

typeof(X) · x.GetType() → Type { Name, FullName, ToString(), == }

typeof(X) and x.GetType() answer a Type, which is the type's name: Name ("Int32"), FullName ("System.Int32"), ToString() and ==. GetType() is the type the value IS at run time. Inside a generic class, typeof(T) and `x is T` read the type the instance was built over; a caught exception's GetType() is the type it was thrown as. The rest of reflection is not available yet.

stable3 examples compiled by CItypescsharpreflection

Summary#

A Type names a type:

class Shape { public string Label = ""; }
class Circle : Shape { }
class Square : Shape { }

class Bin<T> where T : Shape {
  public string Holds() => typeof(T).Name;
  public bool Fits(Shape s) => s is T;
}

[Test]
void A_Type_Is_Its_Name() {
  Assert.Equal("Int32", typeof(int).Name);
  Assert.Equal("System.String", typeof(string).FullName);
  Shape s = new Circle();
  Assert.Equal("Circle", s.GetType().Name);              // what it IS, not what it is held as
  Assert.True(s.GetType() == typeof(Circle));
  var bin = new Bin<Circle>();
  Assert.Equal("Circle", bin.Holds());
  Assert.True(bin.Fits(new Circle()));
  Assert.False(bin.Fits(new Square()));
}

Signature#

typeof(X)          // a Type: Name, FullName, ToString(), ==
x.GetType()        // the type x is at run time
typeof(T) / x is T // inside a generic class: what this instance was built over

Description#

What can I ask a Type?#

Its Name (without namespace), its FullName, ToString() (the full name) and whether it is == another. A built-in answers its .NET name (int is System.Int32). The rest of C#'s reflection — GetProperties(), IsClass, BaseType, … — is not available yet and is a compile error that says so; read-only reflection over a type's members is planned.

What does GetType() answer for a value held as its base?#

The type it actually is — Shape s = new Circle(); s.GetType() is Circle. On a null value it throws NullReferenceException, as in C#.

That holds across a package too: a kit's function that takes a Worker and is handed an instance of your app's class Robot : Worker answers Robot, though the kit was written without ever seeing it. The class is read from the value.

What does a caught exception's GetType() answer?#

The type it was thrown as, named as C# names it — the line every catch reaches for first:

class ParcelLostException : NotFoundException { }

string Why(int n) {
  try {
    if (n == 1) throw new InvalidOperationException("the parcel is sealed");
    throw new ParcelLostException("no parcel 7");
  } catch (Exception e) {
    Log.Warning(e.GetType().Name + ": " + e.Message);
    return e.GetType().Name;
  }
}

[Test]
void A_Caught_Exception_Knows_Its_Type() {
  Assert.Equal("InvalidOperationException", Why(1));
  Assert.Equal("ParcelLostException", Why(2));
  Assert.Equal("System.InvalidOperationException", typeof(InvalidOperationException).FullName);
  Assert.Equal("System.Collections.Generic.KeyNotFoundException", new KeyNotFoundException("k").GetType().FullName);
}

A .NET exception's FullName is its .NET one (System.InvalidOperationException, the base System.Exception), a platform exception's is Osysharp.NotFoundException, and yours carries the namespace you declared it in. It is the same on the server and in a component's action, and e.GetType() == typeof(InvalidOperationException) holds.

How does a generic class know its T?#

Each instance carries the types it was built over, set where it is created — so typeof(T) and x is T inside the class's own members answer for THIS instance, including one created inside another generic class from that class's own type parameter (new Box<U>(item) inside Wrap<U>). A static member has no instance, so there they are an error that says to make the type the member's own type parameter.

What is a generic class called?#

What .NET calls it: its name with its arity — Box1forBox<T>, whatever it was built over. typeof` takes a constructed or an open generic as C# does:

class Box<T> { public T Value; public Box(T v) { Value = v; } }

[Test]
void A_Generic_Class_Is_Named_With_Its_Arity() {
  Assert.Equal("Box`1", new Box<int>(1).GetType().Name);
  Assert.Equal("Box`1", typeof(Box<string>).Name);
  Assert.Equal("Box`1", typeof(Box<>).Name);
}

See also#

Related

nameof

The C# compile-time name fold: validates the symbol and folds to its simple-name string literal — the last identifier…

Generic classes

A class can declare type parameters, so one shape serves every type it is used with instead of being copied per entity…

Testing which class a value is

`s is Circle` asks which type a value actually is at run time, answering by the value's own type rather than the type…