[독서 노트].NET 본질론 제3장 - Type Basics(Part 3)
18889 단어 .net
한 가지 흥미로운 점은 서로 다른 기류로부터 CLR에 계승되어 서로 다른 의미가 있다는 것이다.예를 들어 값 유형은 System에서ValueType은 상속되고 모든 객체를 System에서 봉인할 수 있습니다.MarshalByRefObject 계승, 그리고 시스템.ContextBoundObject, 응, 그리고 의뢰.
지난 장에서 정태와 관련된 것을 토론할 때 abstract 키워드를 사용하여 유형을 수식하면 이 종류는 실례화될 수 없다. 그러면 이런 종류는 천성적으로 기류로 태어난 운명이다. 만약에 기류로 삼고 싶지 않다면 sealed로 수식하자.음, 이렇게 해서 sealed와 abstract 두 원수는 함께 부딪칠 수 없다. 그렇지 않으면 하나는 천성적으로 기류를 만들고 하나는 기류를 만들지 못하게 한다. 이것은 PK가 되어야 하지 않겠는가. 아마도 너는 이미 지난 글을 보았을 것이다. 사실 이 두 원수는 IL에서 정말 마주쳤다. 그것은 2.0을 실현하기 위해 도입한 static 유형이다. 그러나 주의해라. C# 차원에서 이 두 원수는 함께 놓을 수 없다.
기류 안의 비사적인 구성원은 파생류의 구성원으로 은근히 알려준다.만약에 기류와 파생류가 모두 같은 이름의 필드를 정의한다면 기류에 두 개의 같은 이름의 필드가 나타나지 않는다면 어떻게 접근해야 합니까?그러면 이 필드가 정적 필드인지 실례적 필드인지 먼저 봐야 한다. 정적 필드라면 유형명으로 인용하면 된다.
1: public class Base
2: {
3: public static int _field;
4: }
5: public class Child : Base
6: {
7: public static int _field;
8: public void Test()
9: {
10: Base._field = 5;
11: Child._field = 6;
12: }
13: }
이 필드가 인스턴스 필드라면 어떻게 할까요? 헤헤, C# 이미this와base 키워드를 준비했습니다.
1: public class Base
2: {
3: public int _field;
4: }
5: public class Child : Base
6: {
7: public int _field;
8: public void Test()
9: {
10: // this
11: _field = 5;
12: //
13: this._field = 5;
14: // base
15: base._field = 7;
16: }
17: }
파생 클래스 내부 접근 필드입니다. 외부 접근이라면:
1: Child c = new Child();
2: Base b = c;
3: c._field = 5;
4: b._field = 6;
그럼 c.field 액세스 및 b.field 방문은 어떤 차이가 있습니까? 그들은 사실 모두 같은 대상을 인용하지만, b와 c 두 변수의 유형이 다르기 때문에 그들이 본 계약도 다르다.c를 통한 액세스필드일 때, 차일드가 베이스 클래스에 숨겨져 있기 때문에field, 그래서 여기는 의심할 여지없이 차일드 클래스에 방문한field, b 변수로 접근하면 Base 형식이기 때문에 Child의 계약(또는 공유 인터페이스, 일부 공유 구성원)을 모르기 때문에 Base 클래스의field.실제로 ILDasm을 사용하여 내부 IL 코드를 살펴보겠습니다.
1: // Child _field
2: IL_000a: ldc.i4.5
3: IL_000b: stfld int32 BaseType.Child::_field
4:
5: // Base _field
6: IL_0011: ldc.i4.6
7: IL_0012: stfld int32 BaseType.Base::_field
의심할 여지없이, 이곳의 방문은 번역 기간에 이미 확정되었다.이른바 정적 귀속이다.
위의 프로그램을 컴파일할 때 실제로 우리는 컴파일러가 경고를 생성한 것을 발견했다.
'BaseType.Child._field' hides inherited member 'BaseType.Base._field'. Use the new keyword if hiding was intended.
이 경고는 사실 무시할 수 있다. 차일드에 키워드 new를 붙여서 이것을 표시하라는 뜻이다.필드 필드는 기본 베이스의 동명 필드를 숨깁니다. 실제적으로 new를 추가하지 않으면 컴파일러가 마지막으로 생성한 코드, 메타데이터에 아무런 영향을 주지 않습니다. 컴파일러의 표현 행위에 영향을 줍니다. 컴파일러가 경고를 주지 않습니다.C#에 있는 이 new 키워드는 책임이 너무 많아요. 사실 여기에 new 키워드를 사용하는 것은 적절하지 않아요. 아니면 VB예요.NET가 더 친근해, VB.NET는 이 경우 Shadows 키워드를 사용합니다.
위에서 말한 것은 모두 필드인데, 방법에 대해서는?방법은 필드와 다르다. 필드는 하나의 이름, 하나의 유형, 방법은 파라미터 목록도 있다.방법의 재부팅을 실현해야 하기 때문에 처리 방법 이름의 재사용은 필드 이름의 재사용과 약간 다르다.
CLR은 기본 클래스와 파생 클래스가 같은 이름을 가진 방법에 대해 두 가지 정책이 있는데 그것이 바로 hide-by-signature와 hide-by-name이다.이것은 방법의 메타데이터에hidebysig메타데이터를 추가할지 여부를 통해 이루어진 것이다.말 그대로 하이드-by-signature는 방법명이 같을 뿐만 아니라 서명도 같아야 파생류가 기류 속의 같은 서명을 숨길 수 있는 방법이 독하다는 것이다.만약에 Hide-by-name을 사용한다면 파생류에 하나의 Test 방법만 있다면 기류에 있는 모든 Test 방법은 파라미터가 있든 없든 몇 개의 파라미터가 있든 파생류에 있는 그 Test 방법에 의해 숨겨진다.
이 정책은 컴파일러와 관련이 있습니다. C# 컴파일러에 대해서는 기본값은hide-by-signature이고 VB에 대해서는 기본값입니다.NET 컴파일러는 Overloads(hide-by-signature)와 Shadows(hide-by-name) 키워드를 사용할 수 있습니다. C++에 대한 기본값은 Hide-by-name입니다. 이것은'고전'C++의 문제점으로 결정됩니다.자, 예를 들어 보겠습니다.
1: public class Base
2: {
3: public void Test()
4: {}
5: public void Test(object o)
6: {}
7: }
8: public class Child : Base
9: {
10: public new void Test()
11: {}
12: public void Test(int i)
13: {}
14: }
이것은 사용하는 C#이므로 기본값은 hide-by-signature입니다.
1: Child c = new Child();
2: Base b = c;
3: // Child Test(), Test()
4: c.Test();
5: // Base Test()
6: b.Test();
7: // Child Test(int)
8: c.Test(5);
9: // Base Test(object)
10: c.Test(“hello”);
실제로 ILDasm을 사용해 보십시오. 이 호출 관계는 컴파일할 때 이미 확정되었습니다. 실행 시 귀속과 관련이 없고, CLR은 실행 방법에 대한 동적 귀속을 지원합니다. 이것은 이 책 뒤에 있는 내용에서 논의할 것입니다.
응, 이 장에서 마지막 문제가 남았어. 하나의 계승 트리에서 구조 함수는 어떻게 호출됩니까?
다음 절차를 보십시오. (이 프로그램은.NET 본질론에서 직접 녹음됩니다.)
1: public class Base
2: {
3: public int x = a();
4: public Base()
5: {
6: b();
7: }
8:
9: static int a()
10: {
11: return 2;
12: }
13: private void b() { }
14:
15: }
16: public class D1 : Base
17: {
18: public int y = c();
19: public D1()
20: {
21: d();
22: }
23: static int c()
24: {
25: return 3;
26: }
27: private void d() { }
28: }
29:
30: public class D2 : D1
31: {
32: public int z = e();
33: public D2()
34: {
35: f();
36: }
37: static int e()
38: {
39: return 4;
40: }
41: private void f() { }
42: }
43: public class D3 : D2
44: {
45: public int w = g();
46: public D3()
47: {
48: h();
49: }
50: static int g()
51: {
52: return 5;
53: }
54: private void h() { }
55: }
D3의 구조 함수를 호출하여 실례화할 때 도대체 무슨 일이 일어났을까?
우리는 ILDasm을 사용하여 D3 구조기의 IL 코드를 역컴파일했습니다.
1: IL_0001: call int32 BaseType.D3::g()
2: IL_0006: stfld int32 BaseType.D3::w
3: IL_000b: ldarg.0
4: IL_000c: call instance void BaseType.D2::.ctor()
5: IL_0013: ldarg.0
6: IL_0014: call instance void BaseType.D3::h()
우리는 컴파일러가public int w = g () 이 코드를 구조기의 첫 줄에 삽입한 다음에 D3의 기본 D2의 구조기를 호출한 다음에 D3 구조기에 원래 있는 h 방법을 호출한 것을 발견했다. 실제로 위의 D2, D1,Base의 호출 규칙은 모두 이와 같기 때문에 간단명료하고 하나의 실례화되었지만 실제적으로 일련의 일이 발생했다.
D3.ctor->g()->D2.ctor->e()->D1.ctor->c()->Base.ctor->a()->Object.ctor->b()->d()->f()->h()
모범 사례
상속 등과 관련된 것이 너무 많기 때문에, 만약 당신이 하나의 종류를 설계하고, 당분간 새로운 종류를 파생시키고 싶지 않다면, 반드시 sealed 키워드로 그것을 수식해야 한다. 그 종류에서 새로운 종류를 파생시킬 필요가 있을 때까지 제거해야 한다.그리고 만약에 하나의 클래스가 프로그램 집합 내부에서만 사용되는지 확인되면 인터넷 키로 클래스를 수식하십시오.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
AS를 통한 Module 개발1. ModuleLoader 사용 2. IModuleInfo 사용 ASModuleOne 모듈...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.