0%

Keywords and features

Common Use

  • new
    • Create instance for value type and reference type
      • For int, is used to configure the value to 0
      • Will automatically call the constructor
    • In derived class, hide the inherited method from base class
    • Cannot be override
  • base
    • Access Keywords
    • Call the public or protected methods in base class.
    • If the base class has this method, call that method; If not, recursively call the base class of the base class
  • this
    • Access Keywords
    • Refer to current instance of object
  • using
    • Import namespace (library)
    • Rename the importing namespace
    • Force to release the non-managed resource (Generate a try-finally block, and put the Dispose method in finally block) for a single object
  • About Null
    • null: Means a reference type points to nothing
    • Nullable<T>
      • Is the same with T?
      • Allow value type to support the NullException
    • null object
      • Is a sub class of original class but defined the property IsNull
      • Can implement INullable interface
    • ??
      • (A ?? B): return the non-null object. Check A firstly
  • Data type convert
    • Must be static
    • Will be transferred to a method in IL
    • Explicit
      • Explicit convert a data type
      • public static explicit operator XX(para)
    • Implicit
      • Implicit convert a data type
      • public static implicit operator XX(para)
  • Precompile keywords
    • #if, #else, #elif, #endif
      • Use to determine whether compile a block of code
      • Use ConditionalAttribute could also achieve the conditional compile. Like [Conditional("Debug")]
    • #define, #undef
      • Work with other precompile keywords together, e.g. #define Debug for the conditional compile
    • #warning, #error
      • Add reminders which shows when compiling the code
    • #region, #endregion
    • #pragma

Less Use

  • checked, unchecked
    • Turn on/off the overflow check during the operation and type converting
  • yield
    • Can only appear in a enumerator block
    • yield return is used to return a value in the enumerator. The return type of the method belongs to System.Collections.Generic.IEnumerator<T>, System.Collections.IEnumerable or etc.
    • yield break is used to end the enumerating.
  • lock
    • Use lock(xx) to add a lock for the xx object, so that if other threads want to call this object, they need to wait current thread to finish the process on this object.
    • the object to lock should be a reference type, it’s better to be private
  • unsafe
    • To allow to use *, &, sizeof to avoid the CLR and operate the memory
  • sealed
    • For sealed class, it cannot be inherited by other class
    • For sealed method, it appear with override keyword together
  • Name: is the property of Type
    • Name: type name
    • Property Name: type name containing the namespace
    • AssemblyQualifiedName: type name containing the namespace and assembly

Constant

  • Once const or readonly values are assigned a value, the value couldn’t be changed.
  • const
    • Only support the value type and string, and not support new keyword
    • It should be assigned value when it declares.
    • It should be determined during the compile instead of in CLR (operating the memory).
    • When compile to IL code, it will turn to private static member. Can be get only through the class
  • readonly or static readonly
    • Support any type
    • It could be assigned value in the constructor or when it is declares. Constructor is prior to declares
    • It is compiled to private member and the value is determined in CLR. Can be get through the object

Class and Struct

  • Class
    • Reference type, instance is stored in the managed heap
    • Used to show actions
    • Contains field, property, method, constructor, indexer, operator
  • Struct
    • Value type, instance is stored in the stack
    • Used to store data
    • Contains field, property, method, constructor, indexer, operator
      • Not support constructor without parameters
      • All members should be public
      • Don’t required to use new to instantiate
    • Only support inherit from interface, can override virtual method

Attribute and Property

Actually they have no relationship to each other.

Attribute

  • Is a class, inherit from System.Attribute
    • Support customized, can include new method
  • Initialize during the compile, Stored in metadata, provide support relevant info or show info for the targeted element in CLR
    • Targeted element include: assembly, module, type, property, event, field, method, param, return
    • Use [], support multi attribute
  • The info of attribute can be get through reflector
  • Used for compile command and some actions
  • Example
    • [AttributeUsage()]: Control how to apply customized attributes to targeted element
    • [Flags]: Regard the value of enum as bit flag rather than value
    • [DllImport()]: Call the un-managed code. e.g. Win32 API method
    • [Serializable]: Indicate the element could be serialized
    • [Conditional()]: Support the conditional compile

Property

  • Encapsulation for private field in class

Interface and abstract class

See here for more info.

Class can implement interfaces and inherit another class.

Interface doesn’t inherit from System.Object. Methods in an interface are abstract methods, so interface cannot be instantiated but only be implemented by other classes. Classes implement one interface need to implement all the abstract methods or properties in the interface. It supports the polymorphisn.

For abstract class, it also contains the abstract methods and the abstract class cannot be instantiated but only be inherited. Classes which inherits a abstract class need to implement all the abstract methods. Apart from the abstract methods, the abstract class can also include some public and virtual methods which contains the body. So the subclass can use those methods directly. It supports the polymorphisn.

Some use cases are as follow.

Interface Abstract Class
Provide common method for unrelated classes Provide common elements for related classes
Defines the action Defines the properties, pre-defined methods, common fields
Usually not change after create Can be used in multi-version scenarios

is and as

Subclass converting to base class can be achieved implicitly, while base class converting to subclass needs to be achieved explicitly.

  • is: Is used to determine whether this type can be converted to another type. Return false is object is null.
  • as: Convert the type. Return null if object is null.

Override and Overload

override overload
For subclass to override the virtual/abstract method in base class In one class, contains several methods with the same name
method name and parameters and return type should be same methods with the same name but with different parameters list
Dynamic binding. Method calling is confirmed in CLR. CLR will check the proper method for a base class reference according to type of its instance Static binding. Method calling is confirmed in the compile
For override the generic method, the constrains of generic method in base class will be inherited. Generic method could also serve as the overload method

Deep copy and Shallow copy

Use = for value type is deep copy; Use = for reference type is mainly shallow copy. (String is deep copy)

Inherit interface ICloneable to customize the Object Clone() method. e.g. return a new object

Static and non-static

Use static field to define a Singleton class.

Static Non-static
Class Only contains static field and static method
Cannot be instantiated
Can contains static method and non-static method
Can be instantiated
Method Can only use static field and static method Can use any field and method

Dynamic

Dynamic is the keyword and a class. It can be used for field, property, method, indexer, parameters, return value, local variable. The specific type of the element declared dynamic will be determined during the CLR/DLR.

var dynamic System.object
type will be determined during compile type will be determined in CLR/DLR a instance type
only ca be used for local variable can be used for everything can be used for everything

Dynamic solve the dynamic bind for objects. It combines C# with other dynamic language like IronPython, IronRuby.

1
2
3
ScriptRuntime py = Python.CreateRuntime();
dynamic mypy = py.UseFile("XX.py");
Console.WriteLine(mypy.Method());

Dynamic can also dynamically add a filed or property to a class.

Special Object Types and Methods

System.Object

Object is the top base class for any other classes in .NET C#. The base methods of System.Object contains:

  • GetType
    • the type could use GetMembers and GetMethods to get the instance of this type
  • ToString: get the full name of the type
    • Can be override by subclass
  • Equals, ReferenceEquals, ==: check whether equals
  • MemberwiseClone: Shadow copy
  • GetHashCode
  • Finalize

Equals

  • public virtual bool Equals(object obj)
    • Default it checks whether points to the same instance
    • Will be override by subclass
      • Check null
      • Check ReferenceEquals
      • Check whether the same type
      • Customized compare 2 fields
    • Need to override the GetHashCode the same time
      • A equals B => A.HashCode = B.HashCode
  • public static bool Equals(object objA, object objB)
    • Check whether 2 objs are points to same instance
    • Check whether 2 objs are both null
    • Use objA’s virtual equals method
  • public static bool ReferenceEquals(object objA, object objB): compare whether 2 objects are points to the same instance
    • Can only compare two reference type
    • When compare two value type, will lead to the boxing, then the result is False
  • ==
    • For value type, it compare whether the two values are the same
    • For reference type, it compare whether the two objects point to the same instance

Note: String is a very special type.

Constructor

There are two kinds of constructors. Their IL forms are as follow.

  • .ctor: object constructor
    • Called when new an object
  • .cctor: type constructor (static constructor)
    • Called when need to assign value to static members
    • Customized code in static constructor will be called after assigning values to static members.
    • Accurate time to call
      • precise
        • If the static constructor is implemented
        • The constructor needs to be executed before the first time use the type
      • beforefieldinit
        • If the static constructor is not implemented
        • The constructor can be executed any time

.NET also provide a flexible constructor to allow to set the properties in the constructors.

1
User user = new User {Name = "aa", Age = "bb"};

String

String is a sealed class. It is a reference type but shows value type features when using.

Immutability. Once a string is created in the memory, it cannot change. Any actions including Insert, Substring, ToUpper will create a new string.

String interning. When the CLR loads, it will create a resident pool. This pool won’t be controlled by GC or Every unique string value in code will store in this resident pool. CLR will also create a hash map, the key is the string stored in resident pool and the value is the address. When creating a string value by assigning a unique value, it will store that value in resident pool and assign the address to this string. When assigning a value appeared before, CLR will assign the address checking from hash map to this string.

Note. If a string is assigned dynamically e.g. assigned by other string, the result value of this string won’t be stored in the resident pool. Use System.Intern(xx) can add it to resident pool.

For convert. Some value types already override the ToString method for converting itself to a string. To convert a string to other value types. e.g. Double.Parse(str), Double.TryParse(str, out num), num = Convert.ToDouble(str)

StringBuilder can help to append several strings and convert to a string together.

Enum

System.Enum inherits from System.ValueType. Default, the first element equals to int 0. The value of the element can also equals to bit number 0x00 if the attribute [Flags] is added to the enum. At this time, a enum variable supports the | operation.

Common methods contain

  • GetNames(typeof(X)): Get all names of the enum
  • GetValues(typeof(X)): Get all value of the enum
  • IsDefined(typeof(), XX): Check whether the name of value in the enum

Collections

Collection is a container to control a bunch of objects. The collection can be treated as a whole object and every object can also be get separately.

Collection is a class in .NET. The common methods for most collections types include:

Method Function
Add add new item
Insert insert new item in a specific index
Remove remove a particular item
Clear remove all items
IndexOf/LastIndexOf find the index of a particular item
Sort sort items
GetEnumerator return value when iterated calling
Count the count of all items
Item get a specific item

For a collection, it should implement the IEnumerator interface, which support MoveNext and Reset methods, and Current property. Then in C#, users can use foreach to get the value from the enumerator.

For a collection, it should implement the Synchronized method, which support for the safe action on the object in the multi-thread code.

The category of collections include:

  • Ordered Collection: only implement ICollection
    • Stack
      • Stack<T>
    • Queue
      • Queue<T>
  • Index Collection: implement IList
    • Array
      • Fixed length
      • Only support the same type
      • Several dimension
      • Cannot insert or remove values
    • ArrayList
      • Dynamic length
      • Support different type
      • 1 dimension
      • Can insert or remove values
      • List<T>
  • Key Collection: implement IDictionary
    • Hashtable
      • The key-value pairs have no order
      • Dictionary<TKey, TValue>
    • SortedDictionary
      • The key-value pairs have order.
    • SortedList
      • Support both index and key-value pair
      • SortedList<T>

For customized collection, users can inherit the CollectionBase abstract class or implement interfaces like ICollection, IList.

Delegate, Anonymous Method, Lambda Expression

Delegate is a class which implements a safe type callback method. Delegate is like the method pointer in C++. It defines the return type and parameters list. A delegate type needs to be instantiated and then added other methods. By calling this delegate directly or use its Invoke method, it could callback other methods added to this delegate.

For multi-methods added to the delegate, it is called multicast. Methods could use += or -= to bind or unbind to the delegate. The order of callback those methods is the same with the order to bind to the delegate.

There is some pre-defined delegate in C#, which include

  • EventHandler: void EventHandler(object sender, EventArgs.Empty)
  • Func<T, TResult>: TResult Func(T t)

Event model is also based on delegate. It is a encapsulation for delegate. See here for more examples about delegate and event.

Anonymous method is a method without name but have the expression body.

Delegate keyword can be used to declare an anonymous method. The anonymous method can be assigned to a delegate variable.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
delegate void MyDelegateType(int x, int y);
MyDelegateType myDelegateInstance = delegate(int x, int y)
{
Console.WriteLine(x - y);
}
myDelegateInstance += delegate(int x, int y)
{
Console.WriteLine(x + y);
Console.WriteLine(x * y);
}

Func<float, float> myFunc = delegate(int x)
{
return x + 10;
}

Thread.Start(delegate()
{
// Do some tasks
});

Lambda Expression can also be used to declare an anonymous method. The form is Argument => expression or (Arguments) => {expression}. Compared with using delegate to declare anonymous method, it don’t required to declare the type of the arguments because the compiler will set the type automatically.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
MyDelegateType myDelegateInstance = 

delegate void MyDelegateType(int x, int y);
MyDelegateType myDelegateInstance = (x, y) => Console.WriteLine(x - y);
myDelegateInstance += (x, y) =>
{
Console.WriteLine(x + y);
Console.WriteLine(x * y);
};

Func<float, float> myFunc = x => x + 10;

Thread.Start(() =>
{
// Do some tasks
});

Generic

1
2
public class test<T> {}
public class MyDict<TKey, TValue> {}
  • Advantages
    • Flexible for any data types
    • Handle one kind of data type at one time, avoid problem in type converting
    • Avoid boxing and unboxing process
  • The class cannot be instantiated if the type T isn’t assigned a specific type
  • In CLR
    • The instance of generic happens in the JIT compile
    • When T is value type, JIT will generate different naive code by replace the T with different value types
    • When T is reference type, there will only be one copy of naive code
  • Some basic generic type in FCL
    • in System.Collections.Generic
    • List<T>
    • SortedList<TKey, TValue>
    • Queue<T>
    • Stack<T>
    • Collection<T>
    • Dictionary<TKey, TValue>

Instantiation

When instantiate a parameterized type field, the default keyword can be used, which will return 0 for value type and null for reference type.

1
2
3
4
5
public class test<T>
{
T data = default(T);
}

Constraints

The generic can use where keyword to add constraints for the parameterized type.

1
public class test<T> where T: constraint1, constraint2, ...
Constraint Function
T: struct The type T must be value type
T: class The type T must be reference type
T: class name The type T must be the named class or its derived class
T: interface The type T must contains or implement those interfaces. The interfaces could also be generic interfaces
T: new() The type T must contain a constructor without parameters

Generic Method, interface and delegate

Generic method can exist in a generic class or a non-generic class.

1
2
3
4
5
6
7
class MyClass{
public void ShowInfo<T>(T t) {}
}

// Call above method
MyClass a = new MyClass();
a.ShowInfo<int>(3);

Interface can also have generic features. Some generic collections class implements those generic interfaces. e.g. IList<T>, IComparable<T>

1
2
3
class MyArray<T>: IComparable<T> {}

class MyArray<T>: IComparable<int>, IComparable<String> {}

Delegate can also have generic features. e.g. delegate string MyMethod<T>(T t).

Generic delegate can also use for event. Like EventHandler<TEventArgs>. It is defined as delegate void EventHandler<TEventArgs>(object sender, TEventArgs e)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
using System;

class MyEventArgs: EventArgs
{
public int Num {get; set;}
public bool flag {get; set;}
}

class MyClass {
public event EventHandler<bool> Event1;
public event EventHandler<MyEventArgs> Event2;
}

MyClass a = new MyClass();
a.Event1 += (sender, e) => // Do some action ;
a.Event2 += delegate(object sender, MyEventArgs e)
{
// Do some action
};

Using tricks

  • Use generic collections to improve efficiency
  • Set constraints for generic
  • Use EventHandler<TEventArgs> for event definition
  • Use Nullable<T> for handling null condition
  • Implement IEnumerable<T> for achieving enumerator
  • Consider using generic when using System.Object or System.Type
  • NOT use generic in Web service

Exception

  • try
    • Must use with catch or finally
  • catch
    • Can have several catch blocks
    • Catch content category
      • System.Exception
        • DivideByZeroException
        • StackOverflowException
        • FileNotFoundException
    • Each catch block contains codes to recover from the exception
    • Can use throw to throw the exception
  • finally
    • Execute in the end no matter whether exception happens

System.Exception is a class, which contains

  • Message: string message
  • InnerException: the collections who through the exception
  • StackTrack: the stack of calling the method, where the exception begins

Safety

Safety models structure of .NET

  • Role-Based Security
    • Role-based security is based on the Principle data. Principal is created by Identity.
    • Principle
      • Implement IPrincipal
      • Contains IsInRole and Identity
      • Instance class includes GenericPrincipal for generic users and WindowsPrincipal for Windows users.
    • Identity
      • Implement IIdentity
      • Contains AuthenticationType, IsAuthenticated, and Name
      • Instance class includes GenericIdentity, WindowsIdentity, FormsIdentity for ASP.NET application and PassportIdentity which requires Passport SDK.
    • Use PrincipalPermissionAttribute or PrincipalPermission class to declarative or explicitly check the access
  • Code-Access Security
    • CLR will check the evidence of the application, then determine the permissions of the code according to the security policy. The group of evidence and its permissions forms the code group.
    • Evidence
      • Code’s publisher
      • StrongName
      • Site
      • Application Directory
    • Permissions of code
      • FileIOPermission
      • EnvironmentPermission
      • EventLogPermission
    • Security Policy
      • Enterprise
      • Machine
      • User
      • AppDomain
    • Use Demand, LinkDeman, Assert to declarative or explicitly check the access
  • Windows-User Security

Namespace

FCL Structure

.NET Framework includes CLR (Memory Management, Thread Management and Remote Process) and FCL, which is a object-oriented collection of reusable types. FCL needs to provide API for program and it provides a tree type structure. FCL assemblies is stored as .dll files in disks.

The node of the FCL structure is called namespace, which contains class, struct, enum, delegate, interface. The namespace is only the logic structure of the FCL structure rather than the physical structure of the assembly. The same namespace could stored in different assemblies. Common namespace are as follows.

System Namespace

System namespace contains many basic data type. It includes

  • Object
  • ValueType
    • Int16, Int32, Char, Boolean, Double
  • String
  • Array
  • Delegate
  • Attribute
  • Type
  • Nullable

It also includes many basic services

  • Exception
  • GC
  • AppDomain: provide a virtual boundary for a program
  • Console: control the standard I/O stream
  • Environment: get current OS, machine, folder info
  • Convert: data type convert
  • Math
  • Random
  • TimeZone

System.IO

It contains handle for the file, folder and memory stream.

  • File, FileInfo: create, open, copy, move, delete action for files
  • Dictionary, DictionaryInfo: create, open, copy, move, delete action for folders
  • Path: provide method to handle the string of the folder path
  • Stream: base class of all the stream class
    • Read, Write, Seek, Flush, Close
    • FileStream: specific for file
    • StreamReader, StreamWriter: read and write string to file
    • StringReader, StringWriter: read and write string to memory

System.Runtime.Serialization, System.Xml.Serialization

It is related to the serialization (convert object to a saveable or transmittable format) and deserialization (recover the object from physical material or stream).

  • System.Runtime.Serialization
    • Deep Serialization: serialize all properties and filed
    • BinaryFormatter
    • SoapFormatter
  • System.Xml.Serialization
    • Shadow Serialization: serialize public properties and filed
    • XmlSerializer

System.Diagnostics

Handle the system process, event log and help to debug.

  • Process: control the start and stop of outside program
  • Debug, Trace: trace the running status of program

Reference

  1. 《你必须知道的.NET(第二版)》,第3部分、第4部分,王涛著

Asynchronous method is implemented in C# by using async/await keywords.

Async/Await keywords

That’s the goal of asynchronous: enable code that reads/writes like a sequence of statements, but executes in a much more complicated order based on external resource allocation and when tasks complete.

Without language support, writing asynchronous code required callbacks, completion events, or other means that obscured the original intent of the code. In C#, keywords async and await are used to achieved a asynchronous program. The function return immediately when it meets a await. A code sample is like following.

[VoidReturnAsyncMethods] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
using System;
using System.Threading.Tasks;


namespace TestAsyncIL
{
class TestAsync
{
public async void Method1()
{
Console.WriteLine("Start Method 1");
for (int i = 0; i < 3; i++)
{
await Task.Delay(330);
Console.WriteLine("Method 1: " + i.ToString());
}
}

public async void Method2()
{
Console.WriteLine("Start Method 2");
for (int i = 0; i < 3; i++)
{
await Task.Delay(330);
Console.WriteLine("Method 2: " + i.ToString());
}
}

public void Method3()
{
Console.WriteLine("Start Method 3");
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Method 3: " + i.ToString());
}
}
}

class Program
{
static void Main(string[] args)
{
TestAsync testAsync = new TestAsync();
testAsync.Method1();
testAsync.Method2();
testAsync.Method3();
Console.Read();
/* Output is (replaced '\n' by ',')
Start Method 1, Start Method 2, Start Method 3
Method 3: 0, Method 3: 1, Method 3: 2
Method 1: 0, Method 2: 0, Method 1: 1
Method 2: 1, Method 1: 2, Method 2: 2
*/
}
}
}

How to code executed:

  1. Run Method1, code output method1, then encounter await, get back to the main function
  2. Run Method2, code output method2, then encounter await, get back to the main function
  3. Run Method3, since the running time is less than 330 milliseconds, it will output all the content in Method3
  4. After 330 milliseconds from Method1 returning, Method1 output the next str
  5. After 330 milliseconds from Method2 returning, Method2 output the next str
  6. Repeat 4 and 5 until output all string.

Note. the step 4 and 5 won’t output if we don’t add the ReadKey method in the end because the main thread will ends and those asnyc methods still in the same thread thus end, too.

Analysis of metadata

In IL, we find that the compiler generate 2 classes for Method1 and Method2 called <Method1>d__0 and <Method2>d__1. The two classes are state machines, the implement the interface TStateMachine. The class contains the following elements.

  • Fields
    • int state
    • TestAsync this
    • AsyncVoidMethodBuilder builder
    • TaskAwaiter u__1
    • int 5__1 (the local int i in for loop)
  • Methods
    • .ctor
    • MoveNext
    • SetStateMachine

There are two important data types. System.Runtime.CompilerServices.AsyncVoidMethodBuilder and System.Runtime.CompilerServices.TaskAwaiter

  • AsyncVoidMethodBuilder
    • A struct, value type
    • Represents a builder for asynchronous methods that do not return a value.
    • Its Create method will return a instance of the builder
    • Its Start<TStateMachine> method will begin running the builder with the associated state machine
  • TaskAwaiter
    • A struct, value type
    • Provides an object that can check whether current asynchronous task is completed and waits for the completion of an asynchronous task
    • Properties
      • IsCompleted. Gets a value that indicates whether the asynchronous task has completed.
    • Methods
      • GetResult. Ends the wait for the completion of the asynchronous task.
      • OnCompleted(Action). Sets the action to perform when the TaskAwaiter object stops waiting for the asynchronous task to complete.
      • UnsafeOnCompleted(Action). Schedules the continuation action for the asynchronous task that is associated with this awaiter.

IL execution pipeline

For the Method2 itself, the IL code is as follows

Once we call the Method2, it does the following things.

  1. Initializes a class called <Method2>d__1.
  2. Assign the current instance to <Method2>d__1::this
  3. Call AsyncVoidMethodBuilder::Create<<Method2>d__1> to create a async void instance for the state machine
  4. Set <Method2>d__1::state to -1
  5. Call AsyncVoidMethodBuilder.Start, the passing state machine is the class <Method2>d__1
  6. Call the <Method2>d__1::MoveNext, since the state is -1, output the str, and change state to 0
  7. Once the state is set to 0, initialize the 5__1 field as 0, then call the Task.Delay method, and get awaiter for this task and set it to u__1 field
  8. Check u__1::IsCompleted. If haven’t finish, call AsyncVoidMethodBuilder::AwaitUnsafeOnCompleted<5__1, <Method2>d__1>, which will call <Method2>d__1::MoveNext method when the status of the awaiter is complete
    • Note. I think the AsyncVoidMethodBuilder will set the <Method2>d__1::MoveNext to 5__1::UnsafeOnCompleted(Action)
  9. The next time it call the MoveNext method, the code will execute different logic since the state has been changed.

Asynchronous method return types

Async methods can have the following return types

| void | Task | Task<TResult>
—–|——|——|—————
has return statement | No | Yes | Yes
return statement type | - | Task | Task<TResult>
can assign to delegate or event | Yes | Yes | Yes
can assign to Task | No | Yes | Yes
caller can know whether this method has completed (create a awaiter for it) | No | Yes | Yes
its awaiter has return statement | - | No | Yes
its awaiter return type | - | - | TResult

Task and Task class

Compared to execute a thread to do actions, the Task class is used to execute asynchronously, and it can track the status of this asynchronous action, including whether it finish and what is the return value. Most commonly, a lambda expression is used to specify the work that the task is to perform.

For operations that return statement, the Task<TResult> class will be used.

Samples

[TaskReturnAsyncMethods] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
using System;
using System.Threading.Tasks;


namespace TestAsyncIL
{
struct ReturnArgs
{
public int Data { get; set; }
public string Text { get; set; }
}

class TestAsyncTask
{
public async Task Method1()
{
Console.WriteLine("Start Method 1");
for (int i = 0; i < 3; i++)
{
await Task.Delay(330);
Console.WriteLine("Method 1: " + i.ToString());
}
}

public async Task<ReturnArgs> Method2()
{
Console.WriteLine("Start Method 2");
for (int i = 0; i < 3; i++)
{
await Task.Delay(330);
Console.WriteLine("Method 2: " + i.ToString());
}
return new ReturnArgs() { Data = 2, Text = "Task Finished!" };
}

public void Method3()
{
Console.WriteLine("Start Method 3");
for (int i = 0; i < 3; i++)
{
Console.WriteLine("Method 3: " + i.ToString());
}
}
}

class Program
{
static void Main(string[] args)
{
// mMain1();
// mMain2();
mMain3();
Console.ReadKey();
}

static async void mMain1()
{
TestAsyncTask testAsync = new TestAsyncTask();
testAsync.Method1();
testAsync.Method2();
testAsync.Method3();
/* Output is (replaced '\n' by ',')
Start Method 1, Start Method 2, Start Method 3
Method 3: 0, Method 3: 1, Method 3: 2
Method 1: 0, Method 2: 0, Method 1: 1
Method 2: 1, Method 1: 2, Method 2: 2
*/
}

static async void mMain2()
{
TestAsyncTask testAsync = new TestAsyncTask();
await testAsync.Method1();
ReturnArgs args = await testAsync.Method2();
Console.WriteLine(args.Data.ToString() + " " + args.Text);
testAsync.Method3();
/* Output is (replaced '\n' by ',')
Start Method 1, Method 1: 0, Method 1: 1, Method 1: 2
Start Method 2, Method 2: 0, Method 2: 1, Method 2: 2
2 Task Finished!
Start Method 3, Method 3: 0, Method 3: 1, Method 3: 2
*/
}

static async void mMain3()
{
TestAsyncTask testAsync = new TestAsyncTask();
Task a = testAsync.Method1();
Task<ReturnArgs> b = testAsync.Method2();
Task.WaitAny(a);
var args = await b;
Console.WriteLine(args.Data.ToString() + " " + args.Text);
testAsync.Method3();
/* Output is (replaced '\n' by ',')
Start Method 1, Start Method 2
Method 1: 0, Method 2: 0,
Method 1: 1, Method 2: 1,
Method 1: 2, Method 2: 2,
2 Task Finished!
Start Method 3, Method 3: 0, Method 3: 1, Method 3: 2
*/
}
}
}

In the mMain1 method, the Method1 and Method2 are still be executed in a synchronously. So the output is the same with the void-returning async method.

In the mMain2 method, we create the awaiter for the Method1 and Method2, so Method2 will be called after Method1, and Method3 will be called after Method2.

In the mMain3 method, we firstly run the Method1 and Method2 synchronously and assign them to Task and Task<TResult> variables. So the two methods will run at the same time. Then we use Task.WaitAny and create a awaiter for Method2, so Method3 will run after Method1 and Method2.

Analysis in IL

Similarly, the complier will generate 2 state machine classes for Method1 and Method2 and 3 state machine for mMain1, mMain2, mMain3.

In the state machine of Method1 and Method2, it is similar to the void-returning async method but it uses AsyncTaskMethodBuilder<TResult> classes to build the async task. In Method2, when it is finished, it uses SetResult(TResult) method to marks the task as successfully completed and also pass the ReturnArgs statement.

In the mMain2 and mMain3 state machines, there will be TaskAwaiter class and TaskAwaiter<TResult> result. After the awaiter’s property IsCompleted is true, they will call TaskAwaiter<TResult>.GetResult to get the return statement from the AsyncTaskMethodBuilder<TResult>.

Cancellation token to terminate async thread

The Async function can be cancelled by passing a CancellationToken parameter created by a CancellationTokenSource.

[AsyncWithToken] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
CancellationTokenSource tokenSource;

/// Upload new anchor pipeline
public void UploadNewAnchor()
{
// Create a new token because token can only by cancelled once
tokenSource = new CancellationTokenSource();

// Add the callback methods when the token is cancelled
tokenSource.Token.Register(() => feedbackBox.text = "Cancel Action!");
tokenSource.Token.Register(async () =>
{
CloudManager.StopSession();
await cloudManager.ResetSessionAsync();
});

// Start the async method
UploadNewAnchorAsync(tokenSource.Token);
}

/// Upload anchor process, can be canceled anytime
async void UploadNewAnchorAsync(CancellationToken token)
{
while (!CloudManager.IsReadyForCreate)
{
await Task.Delay(330);
token.ThrowIfCancellationRequested();
}

CloudManager.UploadAnchor();
}

/// Stop the uploading process by the cancellation token
public void StopAction()
{
if (tokenSource != null)
{
tokenSource.Cancel();
tokenSource.Dispose();
}
}

Reference

  1. AsyncVoidMethodBuilder Struct
  2. AsyncTaskMethodBuilder Struct
  3. AsyncTaskMethodBuilder Struct
  4. TaskAwaiter Struct
  5. TaskAwaiter.GetResult Method
  6. Task.GetAwaiter Method
  7. Task.Delay Method

Inheritance is a very important feature in C#. It can allow you to achieve the polymorphisn, which is very very important feature which could help you to write the code in a good design (easy to extend).

Category

  • Inherit a class
    • Inherit a abstract class
      • Must implement the abstract methods in the abstract class
      • Can override/new the virtual methods in the abstract class
    • Inherit a normal class
      • Can override/new the virtual methods in the abstract class
  • Inherit an interface
    • Must implement all members (properties, methods, events) in the interface.

The different field

When class Student inherits class Person publicly. Those functions and members which are public and protected in class Person can be used in class Student. Functions and members which are private in class Person cannot be used in class Student.

However, if there is a public/protected function in class Person, which uses the private members of class Person. When class Student call that functions, those private members in class Person could also be used via this function.

Base function

Use base.function() in the class Student can call the functions implemented in class Person (the functions should be public/protected). This is often used when class Student rewrite the methods of class Person but still need to call the method of class Person.

About override/new keyword

Polymorphisn often happens when you declare a class Person reference, but you point it to an object instantiated from class Student.

When there is a abstract/virtual method in class Person, class Student could use override/new to rewrite that method. At this moment, call the which are rewrote can lead to charming results.

Since the abstract class can’t be instantiated, we just talked about those virtual - override/new relationship.

  • If the reference type and the object type is the same. Call those functions are calling the function in their own field.
  • If the reference type is the base class and the object type is the subclass.
    • If the method is declared override in subclass. Call this method from the reference is executing the method in subclass.
    • If the method is declared new in subclass. Call this method from the reference is executing the method in base class.

If the method is declared abstract in base class. You cannot call it except the subclass override this method.

[InheritNormalClass] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
using System;

namespace testInherit
{
class Person
{
protected int age = 10;
protected int id = 0;
public Person()
{
Console.WriteLine("Person Created!");
}
public void ShowBaseInfo()
{
Console.WriteLine(age.ToString() + ","+ id.ToString());
}

public virtual void ShowType()
{
Console.WriteLine("This is a person");
}

public virtual void ShowName()
{
Console.WriteLine("Person name");
}
}

class Student : Person
{
new int age = 20;
public Student()
{
Console.WriteLine("Student Created!");
}
public void ShowInfo()
{
Console.WriteLine(age.ToString() + ","+ id.ToString());
}

public override void ShowType()
{
Console.WriteLine("This is a student");
}

public new void ShowName()
{
Console.WriteLine("Person name");
}
}

class Program
{
static void Main(string[] args)
{
Student student = new Student();

Console.WriteLine("--------------------");
student.ShowBaseInfo();
student.ShowInfo();
Console.WriteLine("--------------------");
student.ShowType();
Console.WriteLine("--------------------");
Console.WriteLine("--------------------");

Person person = new Person();
Person person_student = student;
Console.WriteLine("--------------------");
person.ShowType();
person_student.ShowType();
student.ShowType();
Console.WriteLine("--------------------");
person.ShowName();
person_student.ShowName();
student.ShowName();
}
}
}
[InheritNormalClass] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
using System;

namespace TestCSharp
{
public class Father
{
public static void DoStaticWork()
{
Console.WriteLine("Father.DoStaticWork");
}

public void DoWork()
{
Console.WriteLine("Father.DoWork");
}

public virtual void DoVirtualWork()
{
Console.WriteLine("Father.DoVirtualWork");
}

public virtual void DoVirtualWork2()
{
Console.WriteLine("Father.DoVirtualWork2");
}

public virtual void DoVirtualWorkAll()
{
Console.WriteLine("Father.DoVirtualWorkAll");
}
}

public class Son : Father
{
public static void DoStaticWork()
{
Console.WriteLine("Son.DoStaticWork");
}
public void DoWork()
{
Console.WriteLine("Son.DoWork");
}

public new virtual void DoVirtualWork()
{
Console.WriteLine("Son.DoVirtualWork");
}

public override void DoVirtualWork2()
{
Console.WriteLine("Son.DoVirtualWork2");
}
public override void DoVirtualWorkAll()
{
base.DoVirtualWorkAll();
Console.WriteLine("Son.DoVirtualWorkAll");
}
}

public class Grandson : Son
{
public override void DoVirtualWork()
{
Console.WriteLine("GrandSon.DoVirtualWork");
}

public override void DoVirtualWork2()
{
Console.WriteLine("GrandSon.DoVirtualWork2");
}
public override void DoVirtualWorkAll()
{
base.DoVirtualWorkAll();
Console.WriteLine("GrandSon.DoVirtualWorkAll");
}
}

class Program
{
static void Main(string[] args)
{
Father son = new Son();
Son.DoStaticWork();

son.DoWork();
son.DoVirtualWork();
son.DoVirtualWork2();
son.DoVirtualWorkAll();

Console.WriteLine("--------------------");

Father gson = new Grandson();

gson.DoWork();
gson.DoVirtualWork();
gson.DoVirtualWork2();
gson.DoVirtualWorkAll();

Console.WriteLine("--------------------");

Father father = new Father();

father.DoWork();
father.DoVirtualWork();
father.DoVirtualWork2();
father.DoVirtualWorkAll();

Console.WriteLine("--------------------");

Son realSon = new Son();

realSon.DoWork();
realSon.DoVirtualWork();
realSon.DoVirtualWork2();
realSon.DoVirtualWorkAll();

Console.WriteLine("--------------------");

Son realgSon = new Grandson();

realgSon.DoWork();
realgSon.DoVirtualWork();
realgSon.DoVirtualWork2();
realgSon.DoVirtualWorkAll();
}
}
}

Reference

本文将简述.NET(包括.NET Framework, .NET Core等)和编程语言C#之间的关系。

Basic Information of .NET

Features of .NET

.NET的命名就是因为.net是互联网中比较通用的一个域名后缀,因此.NET本身也是和通用相关的。

  • 跨语言:即只要是面向.NET平台的编程语言((C#、Visual Basic.NET、C++/CLI、Eiffel、F#、IronPython、IronRuby、PowerBuilder、Visual COBOL 以及 Windows PowerShell)),用其中一种语言编写的类型可以无缝地用在另一种语言编写的应用程序中的互操作性。
  • 跨平台:一次编译,不需要任何代码修改,应用程序就可以运行在任意有.NET框架实现的平台上,即代码不依赖于操作系统,也不依赖硬件环境。

.NET Technical Framework System

.NET是一个微软搭造的开发者平台,它主要包括:

  1. 支持(面向)该平台的编程语言(如C#、Visual, Basic、C++/CLI、F#、IronPython、IronRuby…)
  2. 用于该平台下开发人员的技术框架体系(.NET Framework、.NET Core、Mono等),
    1. 定义了通用类型系统,庞大的CTS体系
    2. 用于支撑.NET下的语言运行时的环境:CLR
    3. NET体系技术的框架库FCL
  3. 用于支持开发人员开发的软件工具(即SDK,如VS2017、VS Code等)

Common Type System (CTS)

在.NET的三部分内容中,编程语言和软件工具我们都比较清楚,那么技术框架体系中的具体内容都是什么呢?

  • CLS: (Common Language Specification) 公共语言规范, 在面向.NET开发中,语言互操作的标准规范
  • CTS: (Common Type System) 公共类型系统, 包含:
    • 建立用于跨语言执行的框架。
    • 提供面向对象的模型,支持在 .NET 实现上实现各种语言。
    • 定义处理类型时所有语言都必须遵守的一组规则(CLS)。
    • 提供包含应用程序开发中使用的基本基元数据类型(如 Boolean、Byte、Char 等)的库。
    • 支持CTS,那么我们就称它为面向.NET平台的语言
      CLI(Common Language Infrastructure),是微软将CTS等内容提交给国际组织计算机制造联合会ECMA的一个工业标准。
·

Common Language Runtime (CLR)

  • CLR:.NET虚拟机
    • CLR是.NET类型系统的基础,所有的.NET技术都是建立在此之上
    • 在我们执行托管代码之前,总会先运行这些运行库代码,通过运行库的代码调用,从而构成了一个用来支持托管程序的运行环境,进而完成诸如不需要开发人员手动管理内存,一套代码即可在各大平台跑的这样的操作。
    • 这套环境及体系之完善,以至于就像一个小型的系统一样,所以通常形象的称CLR为”.NET虚拟机”。那么,如果以进程为最低端,进程的上面就是.NET虚拟机(CLR),而虚拟机的上面才是我们的托管代码。换句话说,托管程序实际上是寄宿于.NET虚拟机中
    • C# 编写的程序如果想运行就必须要依靠.NET提供的CLR环境来支持
  • CLR宿主进程:容纳.NET虚拟机的进程,该程序称之为运行时主机

下图是代码编译和运行的流程

Framework Class Library (FCL)

框架库是一些用于描述数据类型的基础类型。比如Object不仅是C#语言的类型根、还是VB等所有面向.NET的语言的类型根,它是整个FCL的类型根。同时.NET的框架库有单继承的特点。

  • BCL(Base Class Library)基础类库
    • 通过.NET语言下编写的一些类库
    • 多都包含在System命名空间下。
    • 包含:基本数据类型,文件操作,集合,自定义属性,格式设置,安全属性,I/O流,字符串操作,事件日志等的类型
  • FCL(Framework Class Library)框架类库
    • FCL中大部分类都是微软通过C#来编写的。
    • 用于网站开发技术的 ASP.NET类库,该子类包含webform/webpage/mvc,
    • 用于桌面开发的 WPF类库、WinForm类库
    • 用于通信交互的WCF、asp.net web api、Web Service类库等等

.NET Implementations

  • .NET Framework
    • A .NET implement coupled with Windows System
    • Only support different version of Windows
  • .NET Core
    • A open source .NET implementation for several desktop OS including Windows, macOS, and Linux, but NOT support mobile OS like Android or iOS.
    • Include a cross-platform CoreCLR
    • Implement subset of the .NET Framework library (decouple with Windows), and add new features (for common usage of different OS)
      • e.g. Not include WPF which coupled with Windows
      • UWP: Specific for Windows System
        • Cross Devices but only runs in Windows.
    • Better in the functions than Mono
  • Xamarin
    • A open source .NET implementation by for several OS including AndroidIOS and macOS
    • It is based on Mono BCL. Unity3d adopts this framework. It will compile the C# code to IL, and compile to naive code according to the target platform
    • Higher efficiency than .NET Core
  • .NET Standard
    • A set of fundamental APIs that all.NET implementations should implement
    • Cannot build the real program, but serve as the library for .NET implementations above

C# and .NET

C# is the programming language targeted to .NET. The version of C# is updated with the update of .NET implementations.

Reference

[1] https://www.cnblogs.com/1996V/p/9037603.html
[2] https://blog.csdn.net/MePlusPlus/article/details/76242330
[3] .NET Standard

Description

Delegate and event are good ways to “subscribe” some actions to other particular actions carried out by the user or the code. It sounds like function 1 is executed firstly, then function 2, 3, and etc are executed following. Those functions may not in the same class.

For example, they are in class A, class B, class C. If we use the traditional ways to achieve this relationship, we need to call function 2 and 3 in the class A when the function 1 is executed. In this way we will increase the degree of coupling between these classes, that is class A cannot work normally without class B and C.

Therefore, we proposed the ideas of delegate and event. Class B and C could “subscribe” the action publisher in class A with their function 2 and 3. When the action 1 is executed, the class A will call the action publisher, then the class B and C will receive the information that action 1 is executed, so they will execute the action 2 and 3.

Comparison between delegate and event

  • Commons
    • They are like the pointer of methods.
    • A method can be used += and -= to be assigned or unassigned to a delegate, and subscribe or unsubscribe the event
    • They both accept anonymous methods (delegate declared, or lambda expression)
    • Event is a specific kind of delegate which will be safer
  • Differences
delegate event
Functions can use += to subscribe it, and -= to unsubscribe it, and = to fully occupy the delegate Only support += and -=
Is a type, still need to instantiate a member Is a variable instance
Can be defined outside the class Can only be defined inside the class
Can be executed outside the class Only can be executed in the class where it defines
delegate is the member of the class (static member) event belongs to the instance of a class
Can be invoked outside the class Cannot be invoked outside the class

Generic delegate

1
2
3
4
5
6
event EventHandler<bool> OnEvent1;

delegate void MyHandler<T>(int a, T t);
event MyHandler<bool> OnEvent2; // Generic variable must be set to a fixed type

Func<float, bool> myFuc;

Delegate declaring anonymous methods

Delegate can be used to declare an anonymous method.

1
2
3
4
5
6
7
event MyHandler<EventArgs> OnTrigger;

OnTrigger += delegate(object sender, EventArgs e)
{
Console.WriteLine(sender);
Console.WriteLine(e);
}

See more about anonymous method from here

Execution

The delegate and event can both be used two methods to executed.

  • Use myDelegateInstance(paras) or myEvent(paras)
  • Use myDelegateInstance.Invoke(paras) or myEvent.Invoke(paras)

They are the same in compiler but the second method could add ? to handle the null conditions.

myDelegateInstance?.Invoke(paras) or myEvent?.Invoke(paras)

Basic Skills

  • Usually set the values of private values in the Start() function.
  • Only gameObject has the function of GetComponent
  • Transform
    • Only transform has the function of Find(string name)
    • The function of Find(string name) for a transform can only find the direct children of a transform

Physics Engine

Collider

Wheel Collider

Wheel collider is a collider rather than the mesh of the wheel. You can add force or torque to the collider to make the collider rotate like a wheel.

Rigidbody

Rigidbody has a element named velocity, which can set the speed(Vector3) of the gameobject.

Detect collision or collision position between objects

  • Use OnCollisionEnter or OnTriggerEnter
    • Required object to have collider and rigidbody
  • Use Physics.OverlapSphere(point) to detect whether the point collides with other colliders.
  • Use Collider.Raycast to get the accurate collision position. It will ignore other colliders but this collider.

Scripting

Find Objects

  • Use transform.root.gameObject can find the root object
  • Use gameObject.GetComponentInChild() can find the script attached to the child objects.
  • Use transform.Find() can find the children objects, but this is only used to find the direct children objects.

Attributes

Attributes of C# can add metadata (information about the types defined in a program) to the program. In Unity, it can be used to show information in Unity editor’s inspector.

The attributes work by placing the name of the attribute enclosed in square brackets ([]) above the declaration of the entity to which it applies. There are several different attributes in Unity

Name Function
HideInInspector Hide public parameter in Unity Inspector
SerializeField Show the private parameter in Unity Inspector
Space Show a space line in Unity Inspector
Header(“XX”) Show a comment for the parameter in Unity Inspector
Range(min, max) Show the value range in Unity Inspector

Asynchronous

Unity can use async/await keywords of C# to achieve asynchronous methods, which can be found here.

However, some Unity library functions cannot be executed correctly in the asynchronous thread. Instead, Unity has its own asynchronous keyword, which is IEnumerator/yield return.

yield return keyword has many kinds of return type, which shows following

Type Function
yield return new WaitForEndofFrame() Wait until the end of current frame
yield return new WaitForFixedFrame() Wait until next frame
yield return new WaitForSeconds(xx) Wait for xx seconds
yield return new WaitForWhile(func) Wait until func return false
yield return new WaitForUntil(func) Wait until func return true

In Unity scripts, sometimes we need to do some actions after changing the properties of the GameObject, e.g. position, rotation. Unity will finish the changing action in the end of current frame. So we need to wait for a while before doing other actions.

Note. the event of delegate trigger is also finished in the current frame.

Also in Unity, we need to use yield return null or allow the code return to the main thread to do some changes in the scene.

Note. don’t use yield return 0 because it is the same with yield return null but it will lead to boxing action.

Compile

Conditional Compile

Unity has many targeted platforms, so the codes can be conditional complied according to this link

Compile Pipeline

  • Unity will compile C# scripts to IL with C# compiler firstly
  • Unity will also combines all the other assemblies like the UnityEngine.dll and mscorlib.dll
  • Unity will use Mono or IL2CPP to build the IL scripts to naive code

Overview

There are several advanced features of C# which are very powerful.

Assembly

An assembly is a collection of types and resources that are built to work together and form a logical unit of functionality. Assemblies are the building blocks of .NET applications. Assemblies has the code that the common language runtime executes.

Assemblies take the 2 forms

  • executable (.exe)
    • <compiler command> <module name>
      • csc test.cs
  • dynamic link library (.dll) files
    • <compiler command> -t:library <module name>
      • csc -t:library test.cs
    • is similar to a class library that contains types that will be referenced by other assemblies
    • has no entry point to begin execution

Note. Each assembly have only one entry point: DllMain, WinMain, or Main.

  • Static assemblies
    • stored on disk in portable executable (PE) files
    • include interfaces, classes, and resources like bitmaps, JPEG files, and other resource files
  • Dynamic assemblies
    • run directly from memory and aren’t saved to disk before execution
    • can save dynamic assemblies to disk after they have executed.

To use an assembly in an application, you must add a reference to it. To add a reference to a assembly. Use the following methods

  • static Load method of the System.Reflection.Assembly
  • GetType method of the Type class can load assemblies
  • Load method of the System.AppDomain class

Once an assembly is referenced, all the accessible types, properties, methods, and other members of its namespaces are available to your application as if their code were part of your source file.

Attributes

Attributes can add metadata (information about the types defined in a program) to the program. An attribute is actually an object that is associated with any of these elements: Assembly, Class, Method, Delegate, Enum, Event, Field, Interface, Property and Struct.

The attributes work by placing the name of the attribute enclosed in square brackets ([]) above the declaration of the entity to which it applies. It can contain parameters and the users can customize the attributes.

See here for more info.

In Unity, a public element with the attribute HideInInspector won’t appear in the inspector of Unity Editor. See here for Unity attribute.

A sample of serialization attribute is in the Serialization and Deserialization below.

Exceptions

See here for more info.

Generics

Generics makes it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code.

A Generics class can also be inherited by other class. The derived class can specify the type of the class or it can inherit without specify the class. Generics class can also add Constrains to give some restrictions of the type.

[testGenerics] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System;
using System.Collections.Generic;

namespace testCSharp
{
class MyGenericClass<type>
{
public type element1;
public MyGenericClass(type value = default(type))
{
element1 = value;
}

public type function1(type input)
{
Console.WriteLine(input);
return element1;
}
}

class MyDerivedClass1: MyGenericClass<string>
{
public MyDerivedClass1(string value)
{
element1 = value;
}
}

class MyDerivedClass2<type>: MyGenericClass<type> where type: class
{
public MyDerivedClass2(type value)
{
element1 = value;
}
}

class Program
{
static void Main(string[] args)
{
MyGenericClass<int> test1 = new MyGenericClass<int>(10);

int value1 = test1.function1(20); // 20
Console.WriteLine(value1); // 10

MyGenericClass<string> test2 = new MyGenericClass<string>("abc");

string value2 = test2.function1("jkl"); //"jkl"
Console.WriteLine(value2); //"abc"

MyDerivedClass1 test3 = new MyDerivedClass1("abc");
string value3 = test3.function1("jkl"); //"jkl"
Console.WriteLine(value3); //"abc"

MyDerivedClass2<int> test4 = new MyDerivedClass2<int>("abc"); //complie error, the 'type' must be a reference type
}
}
}

See here for more about generic.

Interface

Just define the return type and type of parameters, the specific implementation needs to be finish in derived class, which looks like the header file in C++, that only decare but not implement.

1
2
3
4
5
6
7
8
9
10
11
12
interface IMyInterface
{
void MethodToImplement();
}

class InterfaceImplementer : IMyInterface
{
public void MethodToImplement()
{
Console.WriteLine("MethodToImplement() called.");
}
}

See here for more info.

Lambda expression

Lambda expression can be used for anonymous method. The operator => is used.

1
2
3
4
5
6
7
8
9
Func<string> greet = () => "Hello, World!";
Console.WriteLine(greet());

event Handler OnTrigger;
OnTrigger += (sender, e) =>
{
Console.WriteLine(sender);
Console.WriteLine(e);
}

Properties

A property is a member that provides a flexible mechanism to read, write, or compute the value of a private field (Member variables or methods in a class or structures). Properties can be used as if they are public data members, but they are actually special methods called accessors. This enables data to be accessed easily and still helps promote the safety and flexibility of methods.

[PropertiesSimple] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Myclass
{
float seconds;

// Declare a Code property of type string:
public float Hours
{
get
{
return seconds / 3600.0f;
}
set
{
if (value < 0 || value > 24)
throw new ArgumentOutOfRangeException(
$"{nameof(value)} must be between 0 and 24.");

seconds = value * 3600;
}
}
}

In some cases, property get and set only return a value or assign a value, so the properties can be used auto-implemented to write. Note the code below only has the properties but no actual private field set. The required field of class of this kind of code will be set automatically during the compile.

1
2
3
4
5
6
public class SaleItem
{
public string Name { get; set; }

public int Price { get; set; }
}

The properties can also use body expression to simplify. Here we need to use the operator of =>. It could link a member with an expression. Here we don’t need to write the keywords of return but still require an private field.

[BodyExpressionProperties] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class SaleItem
{
string _name;
decimal _cost;

public string Name
{
get => _name;
set => _name = value;
}

public decimal Price
{
get => _cost;
set
{
if (value >= 0)
{
_cost = value;
}
}

}
}

The abstract class may have an abstract property, which can be implemented in the derived class

[AbstractProperties] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public abstract class Person {
public abstract string Name {
get;
set;
}
}

class Student : Person {
private string name = "N.A";
private int age = 0;

// Declare a Name property of type string:
public override string Name {
get {
return name;
}
set {
name = value;
}
}
}

Serialization and Deserialization

It is the process of converting an object into a stream of bytes to store the object or transmit it to memory. The stream of bytes can be deserialized to the object.

To serialize an object, the object should be have a SerializableAttribute attribute. Fields within a class that don’t need to be serialized should include a NonSerializedAttribute attribute.

Users can use binary or XML serialization to serialize an object. Below is a sample to use basic serialization to serialize a class.

[testSerialization] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace testCSharp
{

[Serializable]
class TestSerialize
{
public int ID = 13;
public string content = "test";

[NonSerialized]
public string content2 = "Costum";

public int function()
{
return 0;
}
}

class Program
{
static void Main(string[] args)
{
TestSerialize test1 = new TestSerialize();
test1.ID = 1234;
test1.content = "Hello World";
test1.content2 = "Hello World 2";
IFormatter formatter = new BinaryFormatter();
Stream fs = new FileStream("data.txt", FileMode.Create, FileAccess.Write);

formatter.Serialize(fs, test1);
fs.Close();

fs = new FileStream("data.txt", FileMode.Open, FileAccess.Read);
TestSerialize test2 = (TestSerialize)formatter.Deserialize(fs);

Console.WriteLine(test2.ID); // 1234
Console.WriteLine(test2.content); // Hello World
Console.WriteLine(test2.content2); // None
}
}
}

Thread

One thread if one execution flow for the code. For some parallel work in the same program, we can use multi-thread to save the total execution time.

See here for more information.

LINQ (Language Integrated Query)

Query expression is like the SQL language, it can search elements with some relationship. It starts with from and ends with select or group.

LINQ is the data object language that integrated into C#. It can be used for search, order, compare and summarize action.

[LINQSample] [CSharp]view raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
List<User> users = new List<User>
{
new User{Name = "a", Age = 27},
new User{Name = "b", Age = 32},
new User{Name = "c", Age = 15}
}
Enumerable<Uesr> selectUsers = from user in users
where user.Age < 30
orderby user.Age descending
select user;

// Below is also work
var selectUsers = users.Where(user => user.Age < 30)
.OrderByDescending(user => user.Age).Select(user => user);

LINQ can be used in following scenarios.

  • Objects: object’s in code. e.g. class, collectiosn
  • XML: XML file
  • SQL: SQL database
  • Entities
  • Datasets