참조(Class) 생성자
l IL Code - .ctor
l 메모리 할당 후 0 or null (으)로 초기화
l 상속 X
l virtual, new, override, sealed, abstract 등 키워드 X
l 내부에서 가상메서드 같은 걸 호출하면 안 된다.
– 초기화가 덜 끝난 객체를 만날 시 오류
l 인라인 문법을 사용해 모든 필드 초기화 – 더욱 더 가상메서드 위험이 있음
값(Struct) 생성자
l 생성자가 없어도 되며, 억지로 디폴트 생성자까지 만들지는 않는다.
l 명시적으로 만드는 것을 허용한다.
l 단 생성자에 필드 변수가 없으면 오류 – 어떤 매개변수를 초기화할지 모름
타입 생성자
l IL Code - .cctor
l 정정 생성자, 클래스 생성자, 타입 초기자 등으로 불림
l 인터페이스 적용 가능
l static A() 식 으로 구현 가능 – 구조체에서도 매개변수 없이 구현
l 반드시 private
l JIT 단계에서 만약 해당 함수 호출이력이 있으면 바로 생략하는 식
l 다중 스레드에선 먼저 호출된 스레드에서 해당 생성자에 락을 걺
생성을 한 후 다른 스레드 호출
다른 스레드에서는 이미 호출 이력이 있으므로 해당 함수 생략
l 딱 한번만 호출되는 생성자로서 Singleton 객체 최적화 하기에 최적의 위치
l Static 필드에만 접근이 가능 – 초기화 가능
l 정식적인 static 소멸자는 없음
- 단, System.AppDomain에 DomainUnload 이벤트 처리기 메서드 구현 시 가능
+ 변환 연산자
public A( Int32 num) – Int32 에서 A로 변환
public Int32 ToInt32() – A에서 Int32로 변환
+ 확장 메서드
확장 메서드 - C# 프로그래밍 가이드
확장명 메서드(C# 프로그래밍 가이드)Extension Methods (C# Programming Guide) 이 문서의 내용 --> 확장명 메서드를 사용하면 새 파생 형식을 만들거나 다시 컴파일하거나 원래 형식을 수정하지 않고도 기존 형식에 메서드를 "추가"할 수 있습니다.Extension methods enable you to "add" methods to existing types without creating a new derived type, r
docs.microsoft.com
확장명 메서드를 사용하면 새 파생 형식을 만들거나 다시 컴파일하거나 원래 형식을 수정하지 않고도 기존 형식에 메서드를 "추가"할 수 있습니다. 확장 메서드는 특수한 종류의 정적 메서드이지만 확장 형식의 인스턴스 메서드인 것처럼 호출됩니다. C#, F# 및 Visual Basic에서 작성된 클라이언트 코드의 경우 확장명 메서드를 호출하는 것과 형식에 실제로 정의된 메서드를 호출하는 데는 명백한 차이가 없습니다.
가장 일반적인 확장명 메서드는 쿼리 기능을 기존 System.Collections.IEnumerable 및 System.Collections.Generic.IEnumerable<T> 형식에 추가하는 LINQ 표준 쿼리 연산자입니다. 표준 쿼리 연산자를 사용하려면 using System.Linq 지시문을 사용해서 먼저 범위를 지정합니다. 그러면 IEnumerable<T>을 구현하는 모든 형식에 GroupBy, OrderBy, Average 등의 인스턴스 메서드가 있는 것처럼 나타납니다. List<T> 또는 Array와 같은 IEnumerable<T> 형식의 인스턴스 뒤에 "dot"를 입력하면 IntelliSense 문 완성에서 이러한 추가 메서드를 볼 수 있습니다.
다음 예제에서는 정수 배열에서 표준 쿼리 연산자 OrderBy를 호출하는 방법을 보여 줍니다. 괄호 안의 식은 람다 식입니다. 많은 표준 쿼리 연산자가 람다 식을 매개 변수로 사용하지만 확장명 메서드에 대한 요구 사항은 아닙니다. 자세한 내용은 람다 식을 참조하세요.
C#복사
class ExtensionMethods2 { static void Main() { int[] ints = { 10, 45, 15, 39, 21, 26 }; var result = ints.OrderBy(g => g); foreach (var i in result) { System.Console.Write(i + " "); } } } //Output: 10 15 21 26 39 45
확장명 메서드는 정적 메서드로 정의되지만 인스턴스 메서드 구문을 사용하여 호출됩니다. 확장 메서드의 첫 번째 매개 변수는 메서드가 작동하는 형식을 지정하며 매개 변수 앞에 this 한정자가 있습니다. 확장 메서드는 using 지시문을 사용하여 명시적으로 네임스페이스를 소스 코드로 가져오는 경우에만 범위에 있습니다.
다음 예제에서는 System.String 클래스에 대해 정의된 확장 메서드를 보여 줍니다. 이 확장 메서드는 제네릭이 아닌 비중첩 정적 클래스 내부에서 정의됩니다.
C#복사
namespace ExtensionMethods { public static class MyExtensions { public static int WordCount(this String str) { return str.Split(new char[] { ' ', '.', '?' }, StringSplitOptions.RemoveEmptyEntries).Length; } } }
WordCount 지시문을 사용하여 using 확장 메서드를 범위로 가져올 수 있습니다.
C#복사
using ExtensionMethods;
또한 다음 구문을 사용하여 애플리케이션에서 확장 메서드를 호출할 수 있습니다.
C#복사
string s = "Hello Extension Methods"; int i = s.WordCount();
코드에서 인스턴스 메서드 구문을 사용하여 확장 메서드를 호출합니다. 그러나 컴파일러에서 생성된 IL(중간 언어)이 코드를 정적 메서드 호출로 변환합니다. 따라서 실제로 캡슐화의 원칙을 위반하지 않습니다. 사실상 확장명 메서드는 확장하는 형식의 private 변수에 액세스할 수 없습니다.
자세한 내용은 사용자 지정 확장 메서드를 구현 및 호출하는 방법을 참조하세요.
일반적으로 확장명 메서드를 직접 구현하는 것보다 호출하는 경우가 훨씬 많습니다. 확장 메서드는 인스턴스 메서드 구문을 사용하여 호출되므로 특별한 지식이 없어도 클라이언트 코드에서 확장 메서드를 사용할 수 있습니다. 특정 형식의 확장 메서드를 사용하려면 해당 메서드가 정의된 네임스페이스에 대해 using 지시문을 추가합니다. 예를 들어 표준 쿼리 연산자를 사용하려면 다음 using 지시문을 코드에 추가합니다.
C#복사
using System.Linq;
System.Core.dll에 대한 참조를 추가해야 할 수도 있습니다. 이제 표준 쿼리 연산자가 대부분의 IEnumerable<T> 형식에 사용할 수 있는 추가 메서드로 IntelliSense에 표시됩니다.
컴파일 타임에 확장 메서드 바인딩
확장 메서드를 사용하여 클래스 또는 인터페이스를 확장할 수 있지만 재정의할 수는 없습니다. 이름과 시그니처가 인터페이스 또는 클래스 메서드와 동일한 확장 메서드는 호출되지 않습니다. 컴파일 시간에 확장 메서드는 항상 형식 자체에서 정의된 인스턴스 메서드보다 우선 순위가 낮습니다. 즉, 형식에 Process(int i)라는 메서드가 있고 동일한 시그니처를 가진 확장 메서드가 있는 경우 컴파일러는 항상 인스턴스 메서드에 바인딩합니다. 컴파일러는 메서드 호출을 발견할 경우 먼저 형식의 인스턴스 메서드에서 일치 항목을 찾습니다. 일치 항목이 없으면 형식에 대해 정의된 확장 메서드를 검색하고 찾은 첫 번째 확장 메서드에 바인딩합니다. 다음 예제에서는 컴파일러가 바인딩할 확장명 메서드 또는 인스턴스 메서드를 확인하는 방법을 보여 줍니다.
예제
다음 예제에서는 C# 컴파일러가 메서드 호출을 형식의 인스턴스 메서드 또는 확장명 메서드에 바인딩할 것인지 결정할 때 따르는 규칙을 보여 줍니다. 정적 클래스 Extensions는 IMyInterface를 구현하는 모든 형식에 대해 정의된 확장 메서드를 포함합니다. A, B 및 C 클래스는 모두 인터페이스를 구현합니다.
MethodB 확장 메서드는 이름과 시그니처가 클래스에서 이미 구현된 메서드와 정확하게 일치하므로 호출되지 않습니다.
일치하는 시그니처를 가진 인스턴스 메서드를 찾을 수 없으면 컴파일러는 일치하는 확장명 메서드(있는 경우)에 바인딩합니다.
C#복사
// Define an interface named IMyInterface. namespace DefineIMyInterface { using System; public interface IMyInterface { // Any class that implements IMyInterface must define a method // that matches the following signature. void MethodB(); } } // Define extension methods for IMyInterface. namespace Extensions { using System; using DefineIMyInterface; // The following extension methods can be accessed by instances of any // class that implements IMyInterface. public static class Extension { public static void MethodA(this IMyInterface myInterface, int i) { Console.WriteLine ("Extension.MethodA(this IMyInterface myInterface, int i)"); } public static void MethodA(this IMyInterface myInterface, string s) { Console.WriteLine ("Extension.MethodA(this IMyInterface myInterface, string s)"); } // This method is never called in ExtensionMethodsDemo1, because each // of the three classes A, B, and C implements a method named MethodB // that has a matching signature. public static void MethodB(this IMyInterface myInterface) { Console.WriteLine ("Extension.MethodB(this IMyInterface myInterface)"); } } } // Define three classes that implement IMyInterface, and then use them to test // the extension methods. namespace ExtensionMethodsDemo1 { using System; using Extensions; using DefineIMyInterface; class A : IMyInterface { public void MethodB() { Console.WriteLine("A.MethodB()"); } } class B : IMyInterface { public void MethodB() { Console.WriteLine("B.MethodB()"); } public void MethodA(int i) { Console.WriteLine("B.MethodA(int i)"); } } class C : IMyInterface { public void MethodB() { Console.WriteLine("C.MethodB()"); } public void MethodA(object obj) { Console.WriteLine("C.MethodA(object obj)"); } } class ExtMethodDemo { static void Main(string[] args) { // Declare an instance of class A, class B, and class C. A a = new A(); B b = new B(); C c = new C(); // For a, b, and c, call the following methods: // -- MethodA with an int argument // -- MethodA with a string argument // -- MethodB with no argument. // A contains no MethodA, so each call to MethodA resolves to // the extension method that has a matching signature. a.MethodA(1); // Extension.MethodA(IMyInterface, int) a.MethodA("hello"); // Extension.MethodA(IMyInterface, string) // A has a method that matches the signature of the following call // to MethodB. a.MethodB(); // A.MethodB() // B has methods that match the signatures of the following // method calls. b.MethodA(1); // B.MethodA(int) b.MethodB(); // B.MethodB() // B has no matching method for the following call, but // class Extension does. b.MethodA("hello"); // Extension.MethodA(IMyInterface, string) // C contains an instance method that matches each of the following // method calls. c.MethodA(1); // C.MethodA(object) c.MethodA("hello"); // C.MethodA(object) c.MethodB(); // C.MethodB() } } } /* Output: Extension.MethodA(this IMyInterface myInterface, int i) Extension.MethodA(this IMyInterface myInterface, string s) A.MethodB() B.MethodA(int i) B.MethodB() Extension.MethodA(this IMyInterface myInterface, string s) C.MethodA(object obj) C.MethodA(object obj) C.MethodB() */
일반 지침
일반적으로 반드시 필요한 경우에만 드물게 확장 메서드를 구현하는 것이 좋습니다. 가능하면 기존 형식을 확장해야 하는 클라이언트 코드는 기존 형식에서 파생된 새 형식을 만들어 이 작업을 수행해야 합니다. 자세한 내용은 상속을 참조하세요.
기존 메서드를 사용하여 소스 코드를 변경할 수 없는 형식을 확장하는 경우 형식의 구현이 변경되어 확장명 메서드가 손상될 수도 있습니다.
지정된 형식에 대해 확장 메서드를 구현하는 경우 다음 사항에 유의하세요.
-
시그니처가 형식에 정의된 메서드와 동일한 확장 메서드는 호출되지 않습니다.
-
확장 메서드는 네임스페이스 수준에서 범위로 가져옵니다. 예를 들어 Extensions라는 단일 네임스페이스에 확장 메서드를 포함하는 여러 개의 정적 클래스가 있는 경우 using Extensions; 지시문을 통해 모두 범위로 가져옵니다.
구현된 클래스 라이브러리의 경우 어셈블리의 버전 번호가 증가되는 것을 방지하기 위해 확장 메서드를 사용해서는 안 됩니다. 소스 코드를 소유하고 있는 라이브러리에 중요 기능을 추가하려는 경우 어셈블리 버전 관리를 위한 표준 .NET Framework 지침을 따라야 합니다. 자세한 내용은 어셈블리 버전 관리를 참조하세요.
'C# > CLR via' 카테고리의 다른 글
이벤트 & 제네릭 (0) | 2020.02.28 |
---|---|
매개변수 & 프로퍼티 (0) | 2020.02.21 |
참조 타입과 값 타입 (0) | 2020.02.18 |
Object 그리고 캐스팅 (0) | 2020.02.16 |
CLR과 어셈블리 (0) | 2020.02.16 |