9. Custom Overloaded Operators
When building your own type, you should always consider whether you can use operator overloading
10. When creating objects, consider whether to implement Comparators
If sorting is required, there are two comparators implementations
Class firsttype:icomparable<firsttype>{public string name; public int age; Public Firsttype (Int.) { name = "AA"; This.age = age; } public int CompareTo (Firsttype other) { return other.age.CompareTo (age); }} static void Main (string[] args) { Firsttype f1 = new Firsttype (3); Firsttype F2 = new Firsttype (5); Firsttype F3 = new Firsttype (2); Firsttype f4 = new Firsttype (1); list<firsttype> list = new list<firsttype> { f1,f2,f3,f4 }; List. Sort (); foreach (var item in list) { Console.WriteLine (item);} }
or the second kind.
Class program:icomparer<firsttype>{ static void Main (string[] args) { Firsttype f1 = new Firsttype (3 ); Firsttype F2 = new Firsttype (5); Firsttype F3 = new Firsttype (2); Firsttype f4 = new Firsttype (1); list<firsttype> list = new list<firsttype> { f1,f2,f3,f4 }; List. Sort (new program ()); foreach (var item in list) { Console.WriteLine (item); } } int icomparer<firsttype>. Compare (Firsttype x, Firsttype y) { return X.age.compareto (y.age); }}
It calls the program's compare method.
11. Treat = = and equals differently
Whether it is = = or equals:
For value types, returns True if the value of the type is equal
Returns true for reference types if the type points to the same object
And they can all be overloaded
For a special reference class such as String, Microsoft may consider its practical significance more inclined to a value type, so in the FCL (Framework Class L Ibrary) The comparison of strings is overloaded to a value comparison, not to the reference itself
From the design point of view, many reference types will be similar to the string type of case, such as people, his ID number is the same, then we think is a person, this time we need to overload the Equals method,
In general, for reference types, we want to define an attribute of equal value, just rewrite the Equals method, and let = = refer to equality, so we want to compare which is possible
Because the operator "= =" and "Equals" can be overloaded to "value equality" and "reference equality", in order to be clear, the FCL provides object.referenceequals (); To compare whether two instances are the same reference
12. Rewrite GetHashCode when overriding equals
The dictionary uses a key type of hashcode when judging containskey, so we want to use one of the values in the type as a criterion, we need to re-gethashcode, of course, there are other uses hashcode to determine whether it is equal, If we don't rewrite it, it can produce other effects.
public override int GetHashCode () { //This is written in order to reduce the probability of hashcode repetition, and I am not sure why this is written. Just remember to return (System.Reflection.MethodBase.GetCurrentMethod (). Declaringtype.fullname + "#" + age). GetHashCode ();}
Overriding the Equals method should also be a type-safe interface iequatable<t>, so overriding the final version of equals should be
Class firsttype:iequatable<firsttype>{public string name; public int age; Public Firsttype (Int.) { name = "AA"; This.age = age; } public override bool Equals (object obj) { return age. Equals (((firsttype) obj); public bool Equals (Firsttype Other) { return age. Equals (other.age); } public override int GetHashCode () { return (System.Reflection.MethodBase.GetCurrentMethod (). Declaringtype.fullname + "#" + age). GetHashCode (); }}
13. Format string for type output
Class person:iformattable{public override string ToString () { return ' Default Hello '; } public string ToString (string format, IFormatProvider formatprovider) { switch (format) { case] Chinese ": return" Hello "; Case "中文版": return "Hello"; } return "Helo"; }}
static void Main (string[] args) {person p1 = new Person (); Console.WriteLine (p1); Console.WriteLine (P1. ToString ("Chinese", null)); Console.WriteLine (P1. ToString ("中文版", null));}
After inheriting the IFormattable interface, you can wear parameters in ToString to invoke the different ToString, the default ToString above will not be called to the
There is also a IFormatProvider interface, I have not studied
14, the correct realization of shallow copy and deep copy
Whether it's a deep copy or a shallow copy, Microsoft knows how to explicitly tell the caller by inheriting the ICloneable interface type: The type can be copied
Remember to add the [Serializable] flag before the class [Serializable]class person:icloneable{public string name; Public, child, child; public Object Clone () { //Shallow copy return this. MemberwiseClone (); } <summary>///Deep copy/// I don't know why.///</summary>// <returns></returns > Public Person deepclone () { using (Stream objectstream = new MemoryStream ()) { IFormatter formatter = new BinaryFormatter (); Formatter. Serialize (Objectstream, this); Objectstream.seek (0, seekorigin.begin); Return formatter. Deserialize (objectstream) as Person;}}} [Serializable]class child{public string name; Public Child (string name) { this.name = name; } public override string ToString () { return name; }}
Shallow copy:
The p1.child.name of the output is the changed P2 child.name
Deep copy:
The output is the original
Deep copy is theoretically a reference type for a reference type, but because of the specificity of the reference type, Object.memberwiseclone still creates a copy of it, that is, in a shallow copy, we should treat the string as a value type
15. Use dynamic to simplify reflection implementation
static void Main (string[] args) { //Use reflection Stopwatch watch = Stopwatch.startnew (); person P1 = new person (); var add = typeof (person). GetMethod ("Add"); for (int i = 0; i < 1000000; i++) { Add. Invoke (P1, new object[] {1, 2}); } Console.WriteLine (watch. Elapsedticks); Use dynamic watch. Reset (); Watch. Start (); Dynamic D1 = new person (); for (int i = 0; i < 1000000; i++) { D1. ADD (1, 2); } Console.WriteLine (watch. Elapsedticks);}
It can be seen that using dynamic is more elegant and concise than code written with reflection, and it is more efficient to run multiple times because the dynamic is cached after the first run
Almost 10 times times the difference.
But reflection is more efficient if the number of times is less.
This is the result of running 100 times.
However, many times efficiency is not necessary and it is always recommended to use dynamic to simplify the implementation of reflection
Related articles:
C # Learning Record: Writing high-quality code improvement recommendations 1-3
C # Learning Record: Writing high-quality code improvement recommendations 4-8