extern

'외부의'라는 뜻을 가지는 External의 약자로 외부에 선언된 함수 또는 객체임을 명시하는 역할을 한다.

이때 외부란 C# 코드 이외의 언어로 작성된 코드를 뜻하며 DLL이나 C/C++ 등으로 작성된 함수를 말한다.

이런 외부에서 작성된 코드 즉, C# 및. Net 프레임 워크에서 사용되지 않는 코드 다시 말해서 CLR에서 관리되지 않는 코드를 unmanaged code라고 하는데 이러한 unmanaged code를 불러오기 위해서는 interop 기술이 필요하다.

 

Interop 정리글

 

간략하게 Interop은 다른 언어나 플랫폼에서 작성된 코드를 호출하거나 받는 기술로 COM, P/Invoke, C++/CLI 등이 대표적으로 있는데 extern 키워드는 이러한 함수나 객체가 외부의 것임을 나타내는 역할을 한다.

 

일반적으로 C#에서 많이 사용하는 P/Invoke 방식은 DllImport arttribute와 함께 사용한다.

이 경우 메서드는 반드시 static으로 선언되어야 한다.

// Microsoft AVI File support library
[DllImport("avifil32.dll")] // Interop, P/Invoke
private static extern void AVIFileInit();

 

extern alias

.Net 어셈블리에서 다른 버전의 동일한 어셈블리의 참조가 필요한 경우에 사용할 수 있다.

일반적으로 .Net 프로젝트에서 어셈블리의 참조는 using이나 Imports를 사용하여 참조하는데 만약 서로 다른 버전의 어셈블리를 참조하는 경우에는 컴파일러가 어떤 버전을 사용해야 하는지 구분이 필요하기 때문에 이런 경우 extern alias 키워드를 사용한다.

 

동일한 어셈블리를 버전별로 참조하기 위해서는 각각의 어셈블리를 특정시킬 수 있도록 만드는 작업이 필요하다.

특정시키는 방법으로는 각 어셈블리의 별칭을 지정하는 방법과 이름을 변경하는 방법이 있다.

 

Alias

어셈블리의 별칭을 지정하는 방법은 대표적으로 두 가지 방법이 있다.

 

1. Visual Studio 

비주얼 스튜디오의 솔루션 탐색기에서 별칭을 수정할 수 있다.

Assembly > 우클릭 속성 > Aliases 수정

2. 프롬프트 창

프롬프트에서 어셈블리에 별칭을 부여할 수 있다.

/r:GridV1=grid.dll
/r:GridV2=grid20.dll

 

각각의 어셈블리에 외부에서 사용할 별칭 GridV1과 GridV2가 지정된다.

 

이렇게 별칭을 지정해 주면 extern 키워드를 사용하여 별칭을 참조하면 동일한 어셈블리를 구분해서 참조가 가능해진다.

extern alias GridV1;
extern alias GridV2;
// can using namespace
using Class1V1 = GridV1::Namespace.Class1;
using Class1V2 = GridV2::Namespace.Class1;

 

Name

별칭을 지정하지 않고 이름을 변경하는 방법으로도 참조가 가능하다.

비주얼 스튜디오에서 별칭을 수정했던 설정창을 다시 켜보면 상단에 이름 속성을 확인할 수 있다.

이름을 사용하기 위해서는 별칭이 지정되지 않아야 하므로 별칭을 지우고 이름을 수정한다.

 

그 후에 C#에서 Type.GetType 메서드를 사용하면 어셈블리에서 Type을 가져올 수 있는데 이때 어셈블리의 전체 이름을 사용하여 Type을 가져오면 해당 어셈블리를 특정해서 참조할 수 있게 된다.

Type myType = Type.GetType("MyNamespace.MyType, MyAssembly");

 

728x90
반응형

'Program Language > C#' 카테고리의 다른 글

Interoperability  (0) 2023.02.02
Managed, Unmanaged, Native Code  (0) 2023.02.02
C# 키워드 : delegate, event, action  (0) 2023.01.31
C# 상수 키워드 : const  (0) 2023.01.31
C# 비동기화 키워드 : async, await  (0) 2023.01.31

delegate

C/C++에는 함수를 가리키는 함수 포인터가 있다. 이를 사용하면 함수를 인수로 전달하거나 함수를 반환하는 등 다양한 방식으로 함수를 조작할 수 있는데 C#에서는 포인터를 직접적으로 사용할 수 없다. 그래서 C#에서 함수 포인터를 대신하는 개념으로 메서드를 참조하는 형식의 변수를 선언할 수 있도록 delegate를 사용한다.

 

delegate는 함수 포인터와 비슷한 개념으로 메서드에 대한 참조를 저장하여 메서드를 인수로 전달하거나 메서드를 반환하는 등의 작업을 수행할 수 있다. 메서드 자체를 하나의 변수로 다룰 수 있기 때문에 코드 자체를 참조하는 개념으로 볼 수 있다. 

선언

delegate void DelegateDefault();
delegate int DelegateReturn();
delegate int DelegateReturnParam(int x, int y);

static void Main(string[] args)
{
	DelegateDefault delegateDefault = new DelegateDefault(Print_1);
	DelegateReturn delegateReturn = new DelegateReturn(Print_2);
	DelegateReturnParam delegateReturnParam = new DelegateReturnParam(Print_3);

	delegateDefault();
	int result = delegateReturn();
	int result_2 = delegateReturnParam(1, 2);
}

static void Print_1()
{
	Console.WriteLine("Print_1");
}

static int Print_2()
{
	int num = 2;
	Console.WriteLine("Print_Out" + num);
	return num;
}

static int Print_3(int x, int y)
{
	int sum = x + y;
	Console.WriteLine("Print_Out" + sum);
	return sum;
}

선언된 델리게이트는 동일한 형식으로 선언된 메서드의 참조만 가능하다. 델게이트를 호출하면 참조중인 메서드를 호출하게 된다.

 

delegate chain

또한 델리게이트는 += 연산자를 통해서 동시에 여러 메서드를 참조할 수 있다.

delegate void DelegateChain();
DelegateChain delegateChain;
void Main(string[] args)
{
    delegateChain += Print_1;
    delegateChain += Print_2;
    delegateChain += Print_3;

	// ?.Invoke()를 사용하면 delegateChai이 null인 경우에 에러가 발생하는걸 방지할 수 있다.
    delegateChain?.Invoke();
    
    delegateChain -= Print_3;
    delegateChain -= Print_2;
    delegateChain -= Print_1;
}

static void Print_1() { }
static void Print_2() { }
static void Print_3() { }

참조중인 메서드 중에서 참조를 해제하려면 -= 연산자를 사용하면된다.

 

Anonymouse method call

델리게이트 체인을 활용해서 익명 메서드 호출도 가능하다.

void Main(string[] args)
{
    delegateChain += Print_1;
    delegateChain += Print_2;
    delegateChain += Print_3;
    delegateChain += delegate ()
    {
    	Console.WriteLine();
    };

    delegateChain?.Invoke();
    
	~
}

익명 메서드는 일회성으로 사용될 코드를 간단하게 작성할 때 주로 사용되며 특히 해당 코드가 특별한 용도를 제공하지 않거나 재사용될 필요가 없는 경우에 사용된다.

 

delegate는 대표적으로 이벤트 처리, 비동기 처리, 콜백에 사용된다.

 

Event

프로그래밍에서 이벤트는 어떤 특정한 조건이나 상황이 발생했을 때 이를 감지하고 처리하는 기능을 의미한다.

예를 들어 마우스 클릭, 키보드 입력 등이 이벤트이다. 이벤트 기반 프로그래밍(Event-driven programming)은 이러한 상황에서 반응하듯이 특정한 기능을 수행시키는 이벤트 기반으로 만드는 방식을 말하며 코드와의 상호작용으로 객체 간의 통신을 쉽게하고 객체의 라이프 사이클과 관련된 처리를 간편하게 할 수 있다.

 

Event Subscribe

이벤트 구독

일반적으로 이벤트를 다룰때 이벤트가 발생할때 동작시킬 기능에 해당하는 메서드의 할당과 참조는 외부에서 하더라도 이벤트의 발동은 델리게이트를 선언한 클래스 내에서 하는것이 권장된다.

 

외부에서 delegate에 메서드를 참조시키는걸 Subscribe, 참조한 메서드를 제거하는걸 Unsubscribe 라고 한다.

이렇게 외부에서 메서드를 참조하는 클래스를 Subscriber Class(구독자 클래스), 델리게이트가 선언된 클래스를 Publisher Class(게시자 클래스)라고 부른다.

 

public delegate MyDelegate();
// 게시자 클래스
public class MyPublisher
{
	// 델리게이트
	public MyDelegate MyEvent;
    public void OnEvent()
    {
    	MyEvent?.Invoke();
    }
}

// 구독자 클래스
public class MySubscriber
{
	private Publisher publisher = new Publisher();
	public void Subscirbe()
    {
    	// 구독
    	publisher.MyEvent += SomeMethod; 
    }
    
    public void UnSubscribe()
    {
    	// 해지
 		publicsher.MyEvent -= SomeMethod;
    }
    
    public void SomeMethod(){} 
}

 

event keyword

델리게이트를 이벤트 목적으로 사용할때 event 키워드를 사용한다.

event 키워드를 통해서 delegate가 선언되면 해당 델리게이트는 외부 클래스에서 실행이 불가능하게 된다.

따라서 외부 클래스는 이벤트를 구독과 해지만 가능하며 이벤트의 호출은 선언된 클래스 내부 오직 한곳에서만 일어나기 때문에 안전하게 사용할 수 있게 된다.

 

선언

class EventListner
{
    // 구독할 델리게이트 
    public delegate void EventHandler();
    // event를 통해서 MyEvent는 내부에서만 호출 가능해진다.
    public event EventHandler MyEvent;

    public void OnMyEvent()
    {
        // ? : EventName null check
        EventName?.Invoke();
    }
}

class SubscriberClass
{
	EventLister listner = new EventListenr();
    public void Subscribe()
    {
    	listner.MyEvent += SomeMethod
        // Cannot Call delegate
        lister.MyEvent?.Invoke();
    }
    
    public void SomeMethod(){}
}

 

event 제한자로 선언된 delegate를 외부 클래스에서 직접 호출을 시도하면 에러가 발생한다.

따라서 더 안전하게 이벤트를 구현할 수 있게 해주는 키워드라 할 수 있다.

 

Async

delegate의 비동기 작업은 일반적으로 멀티스레딩을 구현할 때 사용된다. 델리게이트를 통해서 메서드의 실행을 다른 스레드에게 위임하여 해당 스레드에서 비동기적으로 작업을 처리하고, 작업이 완료되면 해당 스레드가 호출한 스레드에게 결과를 알려준다. 이 작업 방식은 보통 결과를 필요로 하지 않는 작업을 빠르고 효율적으로 처리하기 위해서 사용한다.

 

보통 BeginInvoke()와 EndInvoke() 메서드를 활용해서 비동기를 구현한다.

* BeginInvoke : 해당 작업을 다른 스레드에서 비동기적으로 실행

* EndInvoke : 해당 작업이 완료되었는지 확인 후 결과를 반환

 

public delegate int AsyncMethodDelegate(int x, int y);

public class Calculator
{
    public int Add(int x, int y)
    {
        return x + y;
    }

	// Add 메서드를 비동기적으로 실행
    public void AddAsync(int x, int y, AsyncCallback callback)
    {
        AsyncMethodDelegate asyncMethod = new AsyncMethodDelegate(Add);
        // 작업이 종료되면 callback
        asyncMethod.BeginInvoke(x, y, callback, null);
    }
}

public class Program
{
    static void Main(string[] args)
    {
        Calculator calculator = new Calculator();
        AsyncCallback callback = new AsyncCallback(CalculateCompleted);

        calculator.AddAsync(1, 2, callback);
		
        Console.ReadLine();
    }
	
    // callback 호출 함수
    static void CalculateCompleted(IAsyncResult ar)
    {
        AsyncResult result = (AsyncResult)ar;
        AsyncMethodDelegate asyncMethod = (AsyncMethodDelegate)result.AsyncDelegate;

        int resultValue = asyncMethod.EndInvoke(ar);
        Console.WriteLine("Result: " + resultValue);
    }
}

 

Callback Function

콜백 함수는 다른 함수에서 인자로 전달되어 실행되는 함수를 말한다.

일반적으로 다른 함수의 작업이 완료된 후 호출된다.

 

만약 비동기적으로 실행되는 작업이 있을 때 해당 작업이 완료된 후 특정 동작을 실행시키고 싶다고 할때 여기서 콜백 함수를 사용할 수 있다. 비동기 작업을 수행하는 함수는 작업이 완료되면 콜백 함수를 호출하여 완료된 작업에 대한 처리를 수행하게 된다. 

 

콜백 함수는 함수 포인터(delegate)나 람다식 등의 방식으로 전달될 수 있으며 주로 이벤트 처리나 비동기 처리 등에서 사용된다.

 

EventHandler가 대표적인 콜백 함수라 볼 수 있다.

// 버튼 클릭 이벤트 처리를 위한 콜백 함수
private void Button_Click(object sender, EventArgs e)
{
    // 버튼이 클릭되었을 때 실행될 코드
}

// 이벤트 핸들러 등록을 위한 코드
button1.Click += new EventHandler(Button_Click);

버튼이 클릭되면 호출되는 Button_Click 메서드가 콜백 함수로 button1.Click 이벤트에 등록되어 클릭 이벤트가 발생하면 해당 콜백 함수가 실행되는 구조이다. 이렇게 delegate를 사용하여 콜백 함수를 등록함으로써 이벤트 처리와 같이 비동기적인 작업을 보다 효율적으로 처리할 수 있다.

 

Built-in delegate

C#의 내장형 delegate로 일반화된 형태의 델리게이트를 말한다.

매번 델리게이트를 선언하거나 만들 필요 없이 메서드를 매개변수로 전달하고 반환 값을 받을 수 있도록 하는것이 목적이다.

Action

Action은 C#의 내장 델리게이트 중 하나이다. 반환값이 없는 메서드를 참조할 수 있는 타입으로 매개변수를 받아서 반환값이 없는 메서드를 참조하는 기능을 한다.

Func

Func 또한 내장 델리게이트 중 하나로 입력 인자와 반환 값이 있는 일반적인 델리게이트 유형으로 반환 값을 가지는 메서드를 참조할 때 사용한다.

 

단순히 메서드를 전달 또는 메서드를 전달하고 반환 값을 받는 경우에는 Action이나 Func를 사용하는것이 코드를 간소화할 수 있다.

// Action을 사용하여 메서드를 매개변수로 전달하는 예시
public void PrintMessage(Action<string> messageAction)
{
    messageAction("Hello, world!");
}

// 메서드를 Action으로 전달하고 실행
PrintMessage(message => Console.WriteLine(message)); // "Hello, world!" 출력

// delegate를 쓴경우
public delegate void MessageDelegate(string message);	// 델리게이트 생성부터 필요
public void PrintMessage(MessageDelegate messageDelegate)
{
    messageDelegate("Hello, world!");
}

// 메서드를 delegate로 전달하고 실행
PrintMessage(message => Console.WriteLine(message)); // "Hello, world!" 출력


//==============================================================================



// Func을 사용하여 메서드를 매개변수로 전달하고 반환 값을 받는 예시
public int GetStringLength(Func<string, int> lengthFunc, string str)
{
    return lengthFunc(str);
}

// 메서드를 Func으로 전달하고 반환 값을 받습니다.
int strLength = GetStringLength(str => str.Length, "Hello"); // strLength에 5가 저장됩니다.


// delegate를 쓴경우
public delegate int LengthDelegate(string str);
public int GetStringLength(LengthDelegate lengthDelegate, string str)
{
    return lengthDelegate(str);
}

// 메서드를 delegate으로 전달하고 반환 값을 받습니다.
int strLength = GetStringLength(str => str.Length, "Hello"); // strLength에 5가 저장됩니다.

불필요한 delegate의 선언부를 줄일 수 있게 된다..

 

728x90
반응형

const

상수라는 뜻을가지는 Constant에서 따온 키워드이다.

상수란 프로그래밍에서 변하지 않는 값을 의미하는데 한번 값이 정해지면 프로그램이 실행되는 동안 그 값은  항상 일정하다. 컴파일 타임에 값이 결정되므로 런타임 시 메모리를 사용하지 않게 되어 상수를 사용하면 메모리 사용을 줄일 수 있다.

 

public class Program
{
	public static void Main(string[] args)
    {
    	const int A = 10;
        A = 10; // Compiler Error CS0131
    }
}

 

const 키워드로 선언된 변수는 상수로 취급되기 때문에 값을 재할당하면 컴파일 에러가 뜬다. 따라서 상수는 코드 흐름에서도 바뀔 필요가 없고 일정하게 사용될 값이 필요할 때 사용한다.

 

literal const

리터럴 상수 또한 상수와 마찬가지로 변하지 않는 값을 표현할 때 사 용는데 두 개념에는 약간의 차이가 있다.

상수는 const 예약어를 사용해서 변수형태로 선언되고 컴파일 시간에 값이 결정되어 프로그램 중에 값이 변경되지 않는 것이라면 리터럴 상수는 컴파일러가 코드에서 직접 사용할 수 있는 값을 의미한다. 예를 들어서 0, 1, 2, 3 등이 모두 정수형 리터럴 상수로 변수나 상수가 아니면서 프로그램 실행 중에도 변경할 수 없는 값이다.

 

// 상수 PI
const double PI = 3.14159265358979;
// 숫자 리터럴 상수
double radius = 3.0;
// 문자 리터럴 상수
char c = 'C';
// 문자열 리터럴 상수
string str = "Str";

리터럴 상수에는 숫자뿐만 아니라 문자 또한 포함된다.

즉 코드 상에서 선언되지 않았으면서 변수에 할당이 가능한 모든 값을 리터럴 상수라고 할 수 있다.

728x90
반응형

'Program Language > C#' 카테고리의 다른 글

C# 외부 코드 사용 키워드 : extern  (1) 2023.01.31
C# 키워드 : delegate, event, action  (0) 2023.01.31
C# 비동기화 키워드 : async, await  (0) 2023.01.31
C# 클래스 추상화 키워드 : abstract  (0) 2023.01.25
Main method  (0) 2023.01.19

+ Recent posts