C#
Table of Contents
C#, .Net and Mono
Reference
Accessibility Levels
Array
int[] array = new int[] {10, 20, 30, 40};
for (int i = 0; i < array.Length; i++)
{
MessageBox.Show (array[i]);
}
Array.Resize(ref array, 3);
foreach (int element in array)
{
Console.WriteLine(element);
}
int[,] array = new int[4, 2];
int[, ,] array1 = new int[4, 2, 3];
int[, ,] array3D = new int[,,] { { { 1, 2, 3 }, { 4, 5, 6 } },
{ { 7, 8, 9 }, { 10, 11, 12 } } };
// The same array with dimensions specified.
int[, ,] array3Da = new int[2, 2, 3] { { { 1, 2, 3 }, { 4, 5, 6 } },
{ { 7, 8, 9 }, { 10, 11, 12 } } };
// Access like this:
System.Console.WriteLine(array3D[1, 1, 2]);
System.Console.WriteLine(array3Da[1, 0, 1]);
int[,,] a = new int[10,11,12];
Console.WriteLine(a.Length); // 1320
Console.WriteLine(a.GetLength(0)); // 10
Console.WriteLine(a.GetLength(1)); // 11
Console.WriteLine(a.GetLength(2)); // 12
ArrayList
ArrayList
simply stores object references.ArrayList
belongs to the days that C# didn't have generics. It's deprecated in favor ofList<T>
as
- if the conversion isn't possible,
as
returns null instead of raising an exception.
class ClassA { }
class ClassB { }
class MainClass
{
static void Main()
{
object[] objArray = new object[6];
objArray[0] = new ClassA();
objArray[1] = new ClassB();
objArray[2] = "hello";
objArray[3] = 123;
objArray[4] = 123.4;
objArray[5] = null;
for (int i = 0; i < objArray.Length; ++i)
{
string s = objArray[i] as string;
Console.Write("{0}:", i);
if (s != null)
{
Console.WriteLine("'" + s + "'");
}
else
{
Console.WriteLine("not a string");
}
}
}
}
/*
Output:
0:not a string
1:not a string
2:'hello'
3:not a string
4:not a string
5:not a string
*/
Attributes
- Metadata. Data about your objects/methods/properties.
- Attributes are used heavily with reflection.
[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
public class SortOrderAttribute : Attribute
{
public int SortOrder { get; set; }
public SortOrderAttribute(int sortOrder)
{
this.SortOrder = sortOrder;
}
}
[SortOrder(23)]
public class MyClass
{
public MyClass()
{
}
}
public class MyInvestigatorClass
{
public void InvestigateTheAttribute()
{
Type type = typeof(MyClass);
object[] attributes = type.GetCustomAttributes(typeof(SortOrderAttribute), true);
SortOrderAttribute attribute = attributes[0] as SortOrderAttribute;
int sortOrder = attribute.SortOrder;
// Do something with sortOrder
}
}
const
- The initializer of a constant local or a constant field must be a constant expression that can be implicitly converted to the target type
- A constant expression is an expression that can be fully evaluated at compile time
- A
const
object is alwaysstatic
.
delegate
- A
delegate
is a reference type that can be used to encapsulate a named or an anonymous method.
// Declare delegate -- defines required signature:
delegate double MathAction(double num);
class DelegateTest
{
// Regular method that matches signature:
static double Double(double input)
{
return input * 2;
}
static void Main()
{
// Instantiate delegate with named method:
MathAction ma = Double;
// Invoke delegate ma:
double multByTwo = ma(4.5);
Console.WriteLine("multByTwo: {0}", multByTwo);
// Instantiate delegate with anonymous method:
MathAction ma2 = delegate(double input)
{
return input * input;
};
double square = ma2(5);
Console.WriteLine("square: {0}", square);
// Instantiate delegate with lambda expression
MathAction ma3 = s => s * s * s;
double cube = ma3(4.375);
Console.WriteLine("cube: {0}", cube);
}
// Output:
// multByTwo: 9
// square: 25
// cube: 83.740234375
}
event
- Think of events a bit like properties.
- Events are pairs of methods
using System;
class Test
{
public event EventHandler MyEvent
{
add
{
Console.WriteLine ("add operation");
}
remove
{
Console.WriteLine ("remove operation");
}
}
static void Main()
{
Test t = new Test();
t.MyEvent += new EventHandler (t.DoNothing);
t.MyEvent -= null;
}
void DoNothing (object sender, EventArgs e)
{
}
}
- A public
delegate
variable - A
delegate
variable backed by a property - A
delegate
variable withAddXXXHandler
andRemoveXXXHandler
methods
foreach
int[,] numbers2D = new int[3, 2] { { 9, 99 }, { 3, 33 }, { 5, 55 } };
// Or use the short form:
// int[,] numbers2D = { { 9, 99 }, { 3, 33 }, { 5, 55 } };
foreach (int i in numbers2D)
{
System.Console.Write("{0} ", i);
}
// Output: 9 99 3 33 5 55
Int32, Int64
- Both
long
andpointer
are 64-bits - On all platforms,
int
is 32-bits andlong
is 64-bits - You can see this in the names of the underlying types
System.Int32
andSystem.Int64
.
Lambda Expressions
(x, y) => x == y;
(int x, string s) => s.Length > x;
() => SomeMethod();
delegate void TestDelegate(string s);
TestDelegate del = n => { string s = n + " World";
Console.WriteLine(s); };
// Many C# developers use _ to indicate that the parameter isn't going to be used
_ => 10
List<T>
List<string> colors = new List<string>();
colors.Add("Red");
colors.Add("Blue");
colors.Add("Green");
colors.Count;
foreach (string color in colors)
{
MessageBox.Show(color);
}
for (int i = 0; i < colors.Count; i++)
{
MessageBox.Show(colors[i]);
}
colors.Insert(1, "violet");
colors.Sort();
colors.Remove("violet");
if (colors.Contains("Blue"))
{
MessageBox.Show("Blue color exist in the list");
}
string[] strArr = new string[3];
strArr[0] = "Red";
strArr[1] = "Blue";
strArr[2] = "Green";
//here to copy array to List
List<string> arrlist = new List<string>(strArr);
string combindedString = string.Join(",", colors);
string[] arr = colors.ToArray();
arrlist.Clear ();
Method Parameter Keywords
params
- To specify a method parameter that takes a variable number of arguments
- Can send a comma-separated list of arguments of the type
- Can send an array of arguments of the type
- Can send no arguments
public class MyClass
{
public static void UseParams(params int[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.Write(list[i] + " ");
}
Console.WriteLine();
}
public static void UseParams2(params object[] list)
{
for (int i = 0; i < list.Length; i++)
{
Console.Write(list[i] + " ");
}
Console.WriteLine();
}
static void Main()
{
UseParams(1, 2, 3, 4);
UseParams2(1, 'a', "test");
UseParams2();
int[] myIntArray = { 5, 6, 7, 8, 9 };
UseParams(myIntArray);
object[] myObjArray = { 2, 'b', "test", "again" };
UseParams2(myObjArray);
// The following call does not cause an error, but the entire
// integer array becomes the first element of the params array.
UseParams2(myIntArray);
}
}
/*
Output:
1 2 3 4
1 a test
5 6 7 8 9
2 b test again
System.Int32[]
*/
ref
- The
ref
keyword causes an argument to be passed by reference, not by value - To use a
ref
parameter, both the method definition and the calling method must explicitly use theref
keyword
class RefExample
{
static void Method(ref int i)
{
i = i + 44;
}
static void Main()
{
int val = 1;
Method(ref val); // call with 'ref'
Console.WriteLine(val);
// Output: 45
}
}
class CS0663_Example
{
// Compiler error CS0663: "Cannot define overloaded
// methods that differ only on ref and out".
public void SampleMethod(out int i) { }
public void SampleMethod(ref int i) { }
}
class RefOverloadExample
{
// However, overloading can be done
// when one method has a ref or out parameter and the other has a value parameter
public void SampleMethod(int i) { }
public void SampleMethod(ref int i) { }
}
out
- It is like the
ref
keyword, except thatref
requires that the variable be initialized before it is passed.
class OutReturnExample
{
static void Method(out int i, out string s1, out string s2)
{
i = 44;
s1 = "I've been returned";
s2 = null;
}
static void Main()
{
int value;
string str1, str2;
Method(out value, out str1, out str2);
// value is now 44
// str1 is now "I've been returned"
// str2 is (still) null;
}
}
Named and Optional Arguments
- A default value must be one of the following types of expressions:
- a constant expression;
- an expression of the form
new ValType()
, whereValType
is a value type, such as anenum
or astruct
; - an expression of the form
default(ValType)
, whereValType
is a value type. default
keyword, which will returnnull
for reference types andzero
for numeric value types.
namespace OptionalNamespace
{
class OptionalExample
{
static void Main(string[] args)
{
ExampleClass anExample = new ExampleClass();
anExample.ExampleMethod(1, "One", 1);
anExample.ExampleMethod(2, "Two");
anExample.ExampleMethod(3);
ExampleClass anotherExample = new ExampleClass("Provided name");
anotherExample.ExampleMethod(1, "One", 1);
anotherExample.ExampleMethod(2, "Two");
anotherExample.ExampleMethod(3);
// You can use a named parameter
anExample.ExampleMethod(3, optionalint: 4);
}
}
class ExampleClass
{
private string _name;
public ExampleClass(string name = "Default name")
{
_name = name;
}
public void ExampleMethod(int required, string optionalstr = "default string", int optionalint = 10)
{
Console.WriteLine("{0}: {1}, {2}, and {3}.", _name, required, optionalstr, optionalint);
}
}
// The output from this example is the following:
// Default name: 1, One, and 1.
// Default name: 2, Two, and 10.
// Default name: 3, default string, and 10.
// Provided name: 1, One, and 1.
// Provided name: 2, Two, and 10.
// Provided name: 3, default string, and 10.
// Default name: 3, default string, and 4.
}
null
// Traditional null check
var handler = this.PropertyChanged;
if (handler != null)
handler(…)
// equivalent to, and thread-safe thanks to compiler
PropertyChanged?.Invoke(e)
int? length = customers?.Length; // null if customers is null
Customer first = customers?[0]; // null if customers is null
int? count = customers?[0]?.Orders?.Count(); // null if customers, the first customer, or Orders is null
// The ?? operator is called the null-coalescing operator.
// It returns the left-hand operand if the operand is not null;
// otherwise it returns the right hand operand.
int? x = null;
// Set y to the value of x if x is NOT null; otherwise,
// if x = null, set y to -1.
int y = x ?? -1;
Nullable<T>
- The syntax
T?
is shorthand forNullable<T>
, whereT
is a value type. The two forms are interchangeable.
Properties
set
accessor resembles a method whose return type isvoid
. It uses an implicit parameter calledvalue
public class Date
{
private int month = 7; // Backing store
public int Month
{
get
{
return month;
}
set
{
if ((value > 0) && (value < 13))
{
month = value;
}
}
}
}
// From C# 3.0
// Auto-Impl Properties for trivial get and set
public double TotalPurchases { get; set; }
public string Name { get; set; }
public int CustomerID { get; set; }
// From C# 6.0
public string FirstName { get; set; } = "Jane";
PropertyInfo
var ps = typeof(DevSettings).GetProperties();
foreach (PropertyInfo p in ps)
{
if (p.PropertyType == typeof(int))
{
// First parameter fo Get/SetValue is 'obj'
// Using 'null' here because the property is static;
int value = (int)p.GetValue(null, null);
p.SetValue(null, value + 1, null);
}
}
readonly
- A
const
field can only be initialized at the declaration of the field. A
readonly
field can be initialized either at the declaration or in a constructor- readonly only works on class level
Also as a consequence of const requiring a literal, it's inherently static while a readonly field can be either static or instance.
class Age
{
readonly int _year;
Age(int year)
{
_year = year;
}
void ChangeYear()
{
//_year = 1967; // Compile error if uncommented.
}
}
Static Class and Static Class Members
- C# does not support static local variables
- Static members are initialized
- before the static member is accessed for the first time
- before the static constructor, if there is one, is called
public class Automobile
{
public static int NumberOfWheels = 4;
public static int SizeOfGasTank
{
get
{
return 15;
}
}
public static void Drive() { }
public static event EventType RunOutOfGas;
// Other non-static fields and properties...
}
class SimpleClass
{
// Static variable that must be initialized at run time.
static readonly long baseline;
// Static constructor is called at most one time, before any
// instance constructor is invoked or member is accessed.
static SimpleClass()
{
baseline = DateTime.Now.Ticks;
}
}
string
string
is an alias in C# for System.String
. So technically, there is no difference. It's like int
vs. System.Int32
.
this
- To qualify members hidden by similar names
- To pass an object as a parameter to other methods
- To declare indexers
public Employee(string name, string alias)
{
// Use this to qualify the fields, name and alias:
this.name = name;
this.alias = alias;
}
CalcTax(this);
public int this[int param]
{
get { return array[param]; }
set { array[param] = value; }
}
ToString
typeof
- The
typeof
operator cannot be overloaded.
System.Type type = typeof(int);
int i = 0;
System.Type type = i.GetType(); // Get runtime type
// Compare just like any other values
typeField == typeof(string);
typeField == typeof(DateTime);
using
// To allow the use of types in a namespace so that you do not have to qualify
using System.Text;
// To allow you to access static members of a type without having to qualify
using static System.Math;
// To create an alias for a namespace or a type. This is called a using alias directive
using Project = PC.MyCompany.Project;
Compiler Options
define
- Only conditional compilation and pragmas are supported.
// preprocessor_define.cs
// compile with: /define:xx
// or uncomment the next line
// #define xx
using System;
public class Test
{
public static void Main()
{
#if (xx)
Console.WriteLine("xx defined");
#else
Console.WriteLine("xx not defined");
#endif
}
}
Details
Value Types and Reference Types
- There are two categories of value types:
struct
andenum
. - Assigning one value type variable to another copies the contained value.
- Unlike with reference types, you cannot derive a new type from a value type.
- However, like reference types,
structs
can implement interfaces. - A type that is defined as a
class
,delegate
,array
, orinterface
is a reference type.
abstract, virtual, override, sealed
- The
abstract
modifier indicates that the thing being modified has a missing or incomplete implementation. - The
virtual
keyword is used to modify a method, property, indexer, or event declaration and allow for it to be overridden in a derived class. - By default, methods are non-virtual. You cannot override a non-virtual method.
- The
override
modifier is required to extend or modify theabstract
orvirtual
implementation of an inherited method, property, indexer, or event. - You can use
sealed
to prevent them from overriding specificvirtual
methods or properties.
C# Coding Conventions
var currentPerformanceCounterCategory = new System.Diagnostics.
PerformanceCounterCategory();
// Use the + operator to concatenate short strings, as shown in the following code.
string displayName = nameList[n].LastName + ", " + nameList[n].FirstName;
// To append strings in loops, especially when you are working with large amounts of text, use a StringBuilder object.
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
var manyPhrases = new StringBuilder();
for (var i = 0; i < 10000; i++)
{
manyPhrases.Append(phrase);
}
// Use implicit typing for local variables when the type of the variable is obvious from the right side of the assignment,
// or when the precise type is not important.
var var1 = "This is clearly a string.";
var var2 = 27;
var var3 = Convert.ToInt32(Console.ReadLine());
// Preferred syntax. Note that you cannot use var here instead of string[].
string[] vowels1 = { "a", "e", "i", "o", "u" };
// If you use explicit instantiation, you can use var.
var vowels2 = new string[] { "a", "e", "i", "o", "u" };
// If you specify an array size, you must initialize the elements one at a time.
var vowels3 = new string[5];
vowels3[0] = "a";
vowels3[1] = "e";
// This try-finally statement only calls Dispose in the finally block.
Font font1 = new Font("Arial", 10.0f);
try
{
byte charset = font1.GdiCharSet;
}
finally
{
if (font1 != null)
{
((IDisposable)font1).Dispose();
}
}
// You can do the same thing with a using statement.
using (Font font2 = new Font("Arial", 10.0f))
{
byte charset = font2.GdiCharSet;
}
Console.Write("Enter a dividend: ");
var dividend = Convert.ToInt32(Console.ReadLine());
Console.Write("Enter a divisor: ");
var divisor = Convert.ToInt32(Console.ReadLine());
// If the divisor is 0, the second clause in the following condition
// causes a run-time error. The && operator short circuits when the
// first expression is false. That is, it does not evaluate the
// second expression. The & operator evaluates both, and causes
// a run-time error when divisor is 0.
if ((divisor != 0) && (dividend / divisor > 0))
{
Console.WriteLine("Quotient: {0}", dividend / divisor);
}
else
{
Console.WriteLine("Attempted division by 0 ends up here.");
}
// Call static members by using the class name: ClassName.StaticMember.
// This practice makes code more readable by making static access clear.
// Do not qualify a static member defined in a base class with the name of a derived class.
// While that code compiles, the code readability is misleading, and the code may break in the future
// if you add a static member with the same name to the derived class.
Creating delegates manually vs using Action/Func delegates
- The advantage is clarity. By giving the type an explicit name it is more clear to the reader what it does.
- You can specify
ref
/out
parameters unlike the other two generic delegates. - Can have optional parameters.
private delegate double ChangeListAction(string param1, int number);
private Func<string, int, double> ChangeListAction;
private Action<string,int> ChangeListAction;
Use Cases
Can I make local vairalbes constant?
In short, No. Because:
const
only for expressions can be evaluated at compile timereadonly
only works on class level
Can I handle inputs in FixedUpdate?
General Rule:
- Input should be in
Update
, so that there is no chance of having a frame in which you miss the player input (which could happen if you placed it inFixedUpdate
) - Physics calculations should be in
FixedUpdate
, so that they are consistent and synchronised with the global physics timestep of the game (by default 50 times per second) - Camera movement should be in
LateUpdate
, so that it reflects the positions of any objects that may have moved in the current frame
Gaussian Random
// Box–Muller transform
// https://en.wikipedia.org/wiki/Box%E2%80%93Muller_transform
public static float GaussianRandom(float mu, float sigma)
{
float u1 = Random.Range(0.0f, 1.0f);
float u2 = Random.Range(0.0f, 1.0f);
float z0 = Mathf.Sqrt(-2.0f * Mathf.Log(u1)) * Mathf.Cos((2.0f * Mathf.PI) * u2);
return (mu + sigma * z0);
}
General Structure of a C# Program
// A skeleton of a C# program
using System;
namespace YourNamespace
{
class YourClass
{
}
struct YourStruct
{
}
interface IYourInterface
{
}
delegate int YourDelegate();
enum YourEnum
{
}
namespace YourNestedNamespace
{
struct YourStruct
{
}
}
class YourMainClass
{
static void Main(string[] args)
{
//Your program starts here...
}
}
}
Increment operator: Post vs Pre?
- The semantic is not different from C ++
- Thanks to the compiler, In short, there will be no difference in the runtime for control variables
Main Method
static void Main()
{
//...
}
static int Main()
{
//...
return 0;
}
static void Main(string[] args)
{
//...
}
static int Main(string[] args)
{
//...
return 0;
}
Get property name inside setter
using System.Reflection
public static int Dummy {
get {
var propertyName = MethodBase.GetCurrentMethod().Name.Substring(4);
Console.WriteLine(propertyName);
return 0;
}
}
Use string.Substring(4)
to remoe get_
or set_
:
Invoke a method with Reflection
- Must specify parameter types for resolving ambiguity.
BindingFlags.Instance
andBindingFlags.NonPublic
required for a private instance method
IEnumerator Phase(int n)
{
string name = string.Format("Phase{0:00}", n);
BindingFlags bf = BindingFlags.Instance | BindingFlags.NonPublic;
MethodInfo m = typeof(Master).GetMethod(name, bf);
if (m == null)
{
m = typeof(Master).GetMethod("PhaseXX", bf);
}
return (IEnumerator)m.Invoke(this, new object[] {Jukebox.Tempo});
}