Type | Comments | Example |
---|---|---|
-- Integral value types ---- | ||
values true and false | ||
unsigned 8-bit integer | ||
signed 8-bit integer | ||
with short, ushort, uint, long, ulong qualifiers |
|
|
|
nint and unint are signed and unsigned native size integers. Size is platform dependent. | |
|
16 bit unsigned value | |
-- Floating point value types ---- | ||
size = 4 bytes, values have finite precision, and may have approximate values | ||
size = 8 bytes, values have finite precision, and may have approximate values | ||
size = 16 bytes, values are exact, has no exponent | ||
-- Aggregate types ---- | ||
Reference type, array of N elements, all of type |
int first = arr[0]; |
|
Tuple value type, collection of heterogeneous types accessed by position |
char third = tup.Item3; |
|
Value type, collection of heterogeneous types accessed by name |
S s; s.I = 42; s.D = 3.14159; int first = s.I; |
|
-- Reference types defined by C# language ---- | ||
Base for all value and reference types with methods |
string type = o.GetType().ToString(); |
|
Expandable collection of Unicode characters allocated in the managed heap |
|
|
Reference type that encapsulates a method | ||
An instance of dynamic type can be bound to any type of data. Use of a dynamic instance is checked at run-time, not compile-time. |
Console.WriteLine("dyn: {0}", dyn); dyn = 3.1415927; Console.WriteLine("dyn: {0}", dyn); |
|
|
public X(int ia, double da) { i = ia; d = da; } public int i {get;set;} = 0; public double d {get;set;} = 0.0; } X x = new X(42, 3.1415927); Console.WriteLine("X.d: {0}", x.d); |
|
-- Reference types defined by .net library ---- | ||
Expandable list of elements of type |
new List<int> { 1, 2, 3, 2, 1 }; int lfirst = li[0]; |
|
Unordered associative container of Key-Value pairs, held in table of buckets. |
new Dictionary<string, int>() dict["zero"] = 0; |
|
Many additional types defined by library | Types for reading and writing, parallel and concurrent operations, synchronization, ... | |
-- User-defined Types -- | ||
User-defined types | Based on classes and structs, these will be discussed in the next Bit. |
Static typing | All non-dynamic types are known at compile-time and are fixed throughout program execution. |
Dynamic typing | Dynamic types are evaluated at run-time and may be bound to any type of data. |
Type inference |
Compiler infers types in expressions if not explicitly annotated or if declared |
Intermediate strength typing |
Types are exhaustively checked but allow both implicit and explicit conversions.
|
Generics |
Generics provide types and functions with unspecified parameters,
supporting code reuse and abstraction
over types. Generic parameters are specified at the call site, e.g., |
/*---------------------------------
All code used for output has
been elided
*/
/*-- bool --*/
bool b = true;
/*-- int --*/
int i = 42;
/*-- char --*/
char c = 'z';
/*-- double --*/
double d = 3.1415927;
/*-- decimal --*/
decimal dec = 100_000_000.00m;
-------------------------
Value Types
-------------------------
--- bool b = true; ---
b: Type: Boolean
value: True
size: 1
--- int i = 42 ---
i: Type: Int32
value: 42
size: 4
--- char c = 'z' ---
c: Type: Char
value: z
size: 2
--- double d = 3.1415927; ---
d: Type: Double
value: 3.1415927
size: 8
--- decimal dc = 100_000_000.00m; ---
dec: Type: Decimal
value: 100000000.00
size: 16
/*-- array --*/
int[] array = { 1, 2, 3, 2, 1 };
int first = array[0];
/*-- tuple --*/
(int, double, char)tup = (42, 3.14159, 'z');
double second = tup.Item2;
/*-- struct --*/
S s = new S(42, 3.1415927);
int sfirst = s.I;
--- int[]array = { 1, 2, 3, 2, 1} ---
array: Type: Int32[]
value: System.Int32[]
size: 8
{ 1, 2, 3, 2, 1 }
--- (int, double, char)tup = (42, 3.14159, 'z'); ---
tup: Type: ValueTuple`3
value: (42, 3.14159, z)
size: 16
--- S s = new S(42, 3.1415927); ---
s: Type: S
value: CSharpData.Program+S
size: 16
S { 42, 3.1415927 }
/*-- object --*/
object o = new object();
/*-- string --*/
string str = "a string";
string str_alt = new("another string");
/*-- dynamic --*/
dynamic dyn = 42;
dyn = 3.1415927;
/*-- class --*/
X x = new X(42, 3.1415927);
int xFirst = x.i;
--- object o = new object(); ---
o: Type: Object
value: System.Object
size: 8
--- string str = "a string" ---
str: Type: String
value: a string
size: 8
--- dynamic dyn = 42; ---
dyn: Type: Int32
value: 42
size: 4
dyn: Type: Double
value: 3.1415927
size: 8
--- X x = new X(42, 3.1415927); ---
x: Type: X
value: CSharpData.Program+X
size: 8
X { 42, 3.1415927 }
/*-- List<int> --*/
List<int> li = new List<int> { 1, 2, 3, 2, 1 };
int lfirst = li[0];
li.Insert(5, 0);
/*----------------------------------------------
Alias declaration, shown here, must immediately
follow a namespace declaration (see top of file)
- using Dict = Dictionary<string,int>;
This alias is used to simplify Dictionary
declarations below.
*/
Dict dict = new Dict();
dict.Add("three", 3);
dict["zero"] = 0;
dict["one"] = 1;
dict["two"] = 22;
dict["two"] = 2; // overwrites previous value
int oneval = dict["one"];
/*-----------------------------------------
Find first key and value
- this is here just to show how to retrieve
an element from an associative collection
*/
IDictionaryEnumerator enumr =
dict.GetEnumerator();
if(enumr.MoveNext()) { // returns false at end
string key = (string)enumr.Key;
int? value = null;
if(enumr.Value != null) {
value = (int)enumr.Value;
}
// do something with key and value
}
/*-- alternate evaluation --*/
List<string> keys = dict.Keys.ToList();
if(keys.Count > 0) {
string keyfirst = keys[0];
int valfirst = dict[keyfirst];
// do something with key and value
}
/*-- List<int> li = new List<intgt; { 1, 2, 3, 2, 1 }; --*/
li: Type: List`1
value: System.Collections.Generic.List`1[System.Int32]
size: 8
/*-- li.Insert(5, 0) --*/
List<int> { 1, 2, 3, 2, 1, 0 }
/*-- Dict dict = new Dict(); --*/
dict: Type: Dictionary`2
value: System.Collections.Generic.Dictionary`2[ ...
size: 8
dict: { [three, 3], [zero, 0], [one, 1], [two, 2] }
/*--- copy value type ---*/
int i = 42;
string addri = ToStringAddress<int>(&i);
int j = i; // copy of value
string addrj = ToStringAddress<int>(&j);
/*--- copy reference ---*/
List<int> li = new List<int> { 1, 2, 3, 2, 1 };
string addrli = ToStringAddress<List<int>>(&li);
List<int> lj = li; // copy of ref
string addrlj = ToStringAddress<List<int>>(&lj);
-----------------------------------
Demonstrate Copy Operations
-----------------------------------
--- int i = 42; ---
i: address: 0xb2f397e958
--- int j = i; // copy of value ---
j: address: 0xb2f397e948
--------------------------------------------------
Addresses of i and j are unique, demonstrating
value of i was copied to new j location.
--------------------------------------------------
--- List<int> li = new List<int> { 1, 2, 3, 2, 1 } ---
li: address: 0xb2f397e938
--- List<int> lj = li // copy of reference ---
lj: address: 0xb2f397e928
-------------------------------------------------------
Addresses of li and lj are adjacent, and adjacent to
addresses of i and j in the stack frame.
That demonstrates that lj is a copy of the handle li
both of which point to the managed heap-based list
instance.
-------------------------------------------------------
--- lj.Add(-1) ---
lj: { 1, 2, 3, 2, 1, -1 }
li: { 1, 2, 3, 2, 1, -1 }
-------------------------------------------------------
Note: changing lj results in the same change to li.
This demonstrates that both variables refer to the
same List<int> instance in the managed heap.
-------------------------------------------------------
Using ReferenceEquals(li, lj) we find:
li is same object as lj
class Anal {
/*---------------------------------------------
display the type of t using reflection
---------------------------------------------*/
public static void ShowType<T>(
T t, String nm, String suffix = ""
)
{
#pragma warning disable CS8602 // possible null ref
Type tt = t.GetType();
Console.WriteLine("{0}: Type: {1}", nm, tt.Name);
int size = Anal.GetManagedSize(tt);
Console.WriteLine(
"value: {0}\nsize: {1}{2}", t, size, suffix
);
#pragma warning restore CS8602
}
/*---------------------------------------------
return a string with type information
---------------------------------------------*/
public static string GetTypeString<T>(
T t, String nm, String suffix = ""
) {
#pragma warning disable CS8602 // possible null ref
Type tt = t.GetType();
string typeInfo =
String.Format("{0}: Type: {1}\n", nm, tt.Name);
int size = Anal.GetManagedSize(tt);
string instanceInfo =
String.Format(
"value: {0}\nsize: {1}{2}", t, size, suffix
);
return typeInfo + instanceInfo;
#pragma warning restore CS8602
}
/*---------------------------------------------
do t1 and t2 refer to same object?
---------------------------------------------*/
public static void IsSameObj<T>(
T t1, String n1, T t2, String n2,
string suffix = ""
) {
if(ReferenceEquals(t1, t2)) {
Console.WriteLine(
"{0} is same object as {1}{2}",
n1, n2, suffix
);
}
else {
Console.WriteLine(
"{0} is not same object as {1}{2}",
n1, n2, suffix
);
}
}
/*---------------------------------------------
- GetMangedSize(Type type) is function that
returns size of value types and handles.
- It uses advanced techniques that will
eventually be covered elsewhere in this
site. Knowing how it works is not essential
for things we are examining in this demo.
- uses advanced relection
---------------------------------------------*/
// https://stackoverflow.com/questions/8173239/...
public static int GetManagedSize(Type type)
{
var method = new DynamicMethod(
"GetManagedSizeImpl",
typeof(uint), new Type[0],
typeof(TypeExtensions), false
);
ILGenerator gen = method.GetILGenerator();
gen.Emit(OpCodes.Sizeof, type);
gen.Emit(OpCodes.Ret);
var func =
(Func<uint>)method.CreateDelegate(
typeof(Func<uint>)
);
return checked((int)func());
}
/*---------------------------------------------
return string rep of argument's address
---------------------------------------------*/
#pragma warning disable 8500
/*
Suppress warning about taking address of
managed type. Pointer is used only to show
address of ptr as part of analysis of copy
operations.
*/
public static unsafe string
ToStringAddress<T>(T* ptr) {
if(ptr == null) {
return "";
}
IntPtr addr = (IntPtr)ptr;
string addrstr =
string.Format(
"address: 0x" + addr.ToString("x")
);
return addrstr;
}
#pragma warning restore 8500
}
class Display
{
/*---------------------------------------------
show string description of operation
---------------------------------------------*/
public static void ShowOp(
String op, String suffix = ""
) {
Console.WriteLine(
"--- {0} ---{1}", op, suffix
);
}
/*---------------------------------------------
Display all elements of object's tree
- uses advanced reflection
---------------------------------------------*/
// https://stackoverflow.com/questions/7613782/...
public static void Iterate<T>(T t) {
Console.WriteLine("fields:");
foreach(
var field in typeof(T).GetFields(
BindingFlags.Instance |
BindingFlags.NonPublic |
BindingFlags.Public
)
) {
Console.WriteLine(
"{0} = {1}", field.Name, field.GetValue(t)
);
}
Console.WriteLine("methods:");
foreach(
var method in typeof(T).GetMethods(
BindingFlags.Instance | BindingFlags.Public
)
) {
Console.WriteLine(
"{0}", method.Name
);
}
}
/*---------------------------------------------
shorthand for console write command
----------------------------------------------*/
public static void Print(String s = "") {
Console.WriteLine(s);
}
/*---------------------------------------------
Show text wrapped in horizontal lines
---------------------------------------------*/
public static void ShowNote(
string s, string suffix = "", int length = 35
) {
string line = new string('-', length);
Console.WriteLine(line);
Console.WriteLine(" {0}", s);
Console.WriteLine(line + suffix);
}
/*---------------------------------------------
Build string rep of array of type T
---------------------------------------------*/
public static string ToStringRepArray<T>(T[] arr) {
StringBuilder sb = new StringBuilder();
sb.Append("{ ");
bool first = true;
foreach(T item in arr) {
if(item == null) {
break;
}
if(first) {
sb.Append(item.ToString());
first = false;
}
else {
sb.AppendFormat(", {0}", item);
}
}
sb.Append(" }\n");
return sb.ToString();
}
/*---------------------------------------------
Build string rep of IEnumerable collection
T<U>. Works for array too.
---------------------------------------------*/
public static string
ToStringRepIEnumerable<T,U>(T enu)
where T:IEnumerable<U>
{
StringBuilder sb = new StringBuilder();
sb.Append("{ ");
bool first = true;
foreach(U item in enu) {
if(item == null) {
break;
}
if(first) {
sb.Append(item.ToString());
first = false;
}
else {
sb.AppendFormat(", {0}", item);
}
}
sb.Append(" }\n");
return sb.ToString();
}
}
/*-----------------------------------------------------
Direct implementation of enumerating associative
collection. This can also be done with
ToStringRepIEnumerable<Dict,KVPair>(dict).
-----------------------------------------------------*/
public static string
ToStringRepAssocCont<Dict,Key,Value>(Dict assoc)
where Dict:IDictionary<Key,Value>
{
StringBuilder sb = new StringBuilder();
sb.Append("{ ");
bool first = true;
foreach(var item in assoc) {
if(first) {
var sf = String.Format(
"{{ {0}, {1} }}", item.Key, item.Value
);
sb.Append(sf);
first = false;
}
else {
sb.AppendFormat(
", {{ {0}, {1} }}", item.Key, item.Value
);
}
}
sb.Append(" }\n");
return sb.ToString();
}
}
Reference | Description |
---|---|
C# Type System - Microsoft | Discussion with examples |
C# Type Reference - Microsoft | Semi-formal description of C# Types |