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 overDescription#
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#
- nameof — a name folded at compile time
- Generic classes — generic classes and methods
- Testing which class a value is —
is,asand type patterns