집 >백엔드 개발 >C#.Net 튜토리얼 >C# 학습 기록: 고품질 코드 작성 및 개선을 위한 제안 9-15
9. 연산자 오버로드에 익숙해지세요
자신만의 유형을 만들 때 연산자 오버로딩을 사용할 수 있는지 항상 고려해야 합니다
. 정렬이 필요한 경우 두 개의 비교기 구현이 있습니다
class FirstType : IComparable<FirstType> { public string name; public int age; public FirstType(int age) { 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); } }
또는 두 번째 구현
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); } }
Program
==인지 Equals인지 여부입니다. :
값 유형의 경우 유형의 값이 같으면 True를 반환합니다.
참조 유형의 경우 유형이 동일한 개체를 가리키면 True를 반환하고
모두 오버로드될 수 있습니다.
문자열과 같은 특별한 참조 클래스라고 Microsoft에서는 생각할 수 있습니다. 실용적인 의미는 값 유형에 더 가깝기 때문에 FCL(Framework Class Library)에서는 문자열 비교가 값 비교로 오버로드됩니다. 레퍼런스 자체보다
디자인적인 측면에서 많은 레퍼런스 유형이 문자열 유형과 유사합니다. 예를 들어, 동일한 ID 번호를 가진 사람은 이 때 동일한 사람으로 간주됩니다.
일반적으로 참조 유형의 경우 동일한 값의 특성을 정의하고 Equals 메서드만 재정의하고 ==가 참조 동등성을 나타내도록 하여 원하는 것을 비교할 수 있도록 해야 합니다.
연산자 "=="와 "Equals"는 모두 "값 동등"과 "참조 동등"으로 포함되어 재정의될 수 있으므로 명확성을 위해 FCL은 두 인스턴스가 동일한 참조인지 비교하기 위해 object.ReferenceEquals()를 제공합니다.
12. Equals를 다시 작성할 때 GetHashCode도 다시 작성해야 합니다 사전에서 ContainsKey를 판단할 때 키 유형의 HashCode를 사용하므로 해당 유형의 특정 값을 판단 조건으로 사용하려면 다음을 수행해야 합니다. 물론, HashCode를 사용하여 동일한지 판단하는 다른 메서드도 있습니다. 다시 작성하지 않으면 다른 효과가 발생할 수 있습니다public override int GetHashCode() { //这样写是为了减少HashCode重复的概率,至于为什么这样写我也不清楚。。 记着就行 return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + age).GetHashCode(); }Equals 메서드를 다시 작성하는 동안 형식이 안전한 인터페이스 IEquatable
class FirstType : IEquatable<FirstType> { public string name; public int age; public FirstType(int age) { name = "aa"; this.age = age; } public override bool Equals(object obj) { return age.Equals(((FirstType)obj).age); } public bool Equals(FirstType other) { return age.Equals(other.age); } public override int GetHashCode() { return (System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.FullName + "#" + age).GetHashCode(); } }13이어야 합니다. 형식 출력 형식 문자열의 경우
class Person : IFormattable { public override string ToString() { return "Default Hello"; } public string ToString(string format, IFormatProvider formatProvider) { switch (format) { case "Chinese": return "你好"; case "English": 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("English", null)); }
//记得在类前添加[Serializable]的标志 [Serializable] class Person : ICloneable { public string name; public Child child; public object Clone() { //浅拷贝 return this.MemberwiseClone(); } /// <summary> /// 深拷贝 /// 我也不清楚为什么这样写 /// </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; } }얕은 복사: 출력 p1.child.name은 p2의 변경된 child.name입니다딥 복사 : 출력은 원본입니다. Deep 복사본은 이론적으로 참조 유형에 대한 것입니다. 위의 문자열 유형은 참조 유형이지만 참조 유형의 특수성으로 인해 Object.MemberwiseClone은 여전히 복사본을 생성합니다. 이는 얕은 복사 과정에서 문자열을 값 유형으로 간주해야 함을 의미합니다15. 동적을 사용하여 리플렉션 구현을 단순화합니다
static void Main(string[] args) { //使用反射 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); //使用dynamic watch.Reset(); watch.Start(); dynamic d1 = new Person(); for (int i = 0; i < 1000000; i++) { d1.Add(1, 2); } Console.WriteLine(watch.ElapsedTicks); }
차이는 거의 10배입니다
하지만 반사 횟수가 적으면 효율성이 높아집니다이것은 100번 실행한 결과
그러나 많은 경우 효율성은 필요하지 않으며 리플렉션 구현을 단순화하기 위해 항상 동적을 사용하는 것이 좋습니다관련 기사 :위 내용은 C# 학습 기록: 고품질 코드 작성 및 개선을 위한 제안 9-15의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!