선택된 매개변수와 명명된 매개변수
메서드의 매개변수를 정할 때 일부 또는 전체에 대해 기본값을 정할 수 있다.
public static void M(int a = 1, int b = 2, int c = 3) { }
public static void Main()
{
M(); //M(1,2,3)
M(1, c: 2, b: 1); // 콜론(:) 을 이용해서 값을 전할 매개변수를 고를 수 있다.
}
호출 시점에 매개변수를 생략하면 ,C# 컴파일러는 매개변수의 기본값을 포함하는 코드를 생성한다.
모듈 외부에서 불리는 메서드의 경우 기본값을 바꾸는건 위험할 수 있는데, 만약 매개변수의 기본값을 바꾼 경우 호출하는 쪽의 코드도 다시 컴파일 하지 않으면 예전의 기본값을 가진 메서드를 호출하기 때문이다.
따라서
public string GetPath(string fileName = "Untitled")
{
return String.Format(@"C:\{0}.txt", fileName);
}
public string GetPath2(string fileName = null)
{
return String.Format(@"C:\{0}.txt", fileName ?? "Untitled"); // 널 결합 연산자, 널이면 ?? 뒤의 값 반환
}
GetPath 보다는 GetPath2 처럼 쓰는게 더 관리하기 편하다.
참조로 매개변수 전달 (out, ref)
out, ref 키워드는 참조를 넘긴다. out은 변수를 초기화하는데 쓸 수 있다는 점에서 다른데,
public void RefFunc(ref Object obj) { }
public void OutFunc(out Object obj)
{
obj = new object(); //obj 초기화 없으면 컴파일 에러
}
public void Func()
{
Object a;
Object b;
RefFunc(ref a); // 에러 : 할당되지 않은 값 a 를 사용함
OutFunc(out b);
}
CLR에서는 이 둘의 키워드는 동일한 의미이다. 메타데이터 같은 경우에도 ref인지 out인지 구분하는 1비트를 제외하고 같은 값을 가지며 같은 IL코드를 만든다.
즉, IL 혹은 CLR의 관점에서 이 둘은 동일하게 인스턴스의 포인터를 전달한다는 기능을 갖는다. 유일한 차이점은 키워드가 의미하는 바를 문법적으로 확인하는 정도다.
ref 와 out은 오버로드에도 사용할 수 있는데,
public void Add(int a) { }
// 둘 중 하나만 오버로드 할 수 있다.
public void Add(ref int a) { }
public void Add(out int a) { a = 1; }
ref와 out은 CLR관점에서 같은 것이므로 둘 다를 오버로드 할 수는 없다.
가변 매개변수
C#에서는 가변 매개변수를 전달할 수 있다.
public static int Sum(params int[] values)
{
int sum = 0;
if (values != null)
for (int i = 0; i < values.Length; ++i)
sum += values[i];
return sum;
}
public static void Main()
{
Sum(1, 2, 3);
Sum(); // null 이 아니라 new int[0] 이 전달됨
Sum(null);
}
컴파일러는 가변 매개변수를 받으면 내부적으로 배열을 생성하고 모든 매개변수를 배열에 추가한 뒤, 이 배열을 최종 매개변수로 전달하는 일을 대신 수행한다.
그러나 위와같은 과정 때문에 가변 매개변수에 null이 아닌 값을 넣는 경우 성능 저하가 있을 수 있다. 왜냐면 결국 힙에 배열 객체를 할당하고, 요소들을 초기화해야 하며 종국에는 배열의 메모리도 가비지 컬렉션의 대상이 되기 때문이다. 그래서 자주쓰이는 패턴들은 미리 오버로드 형태로 구현해주는게 좋다.
그 예로 System.String 클래스의 Concat 메서드는 다음과 같이 정의되어 있다.
public static String Concat(String str0, String str1);
public static String Concat(String str0, String str1, String str2);
public static String Concat(String str0, String str1, String str2, String str3);
public static String Concat(params String[] values);
기타
매개변수 타입과 반환에 대한 지침.
메서드의 매개변수 타입을 정의할 때는 가능한 가장 추상화된 타입(상속에 있어서 가장 상위의 타입) 이나 인터페이스를 사용하는게 좋다. 당연한 소리지만 재사용할 수 있도록 만든 메서드가 좋다는 의미.
예를들어 List<T>를 매개변수로 사용하는 경우에도 정말 리스트가 필요한게 아니라면 IList<T>를 사용하는게 낫다.
반면에 반환 타입의 경우 가능한 구체적인게 의미가 더 분명하다.
상수화
C++ 같은 비관리 언어의 경우 메서드에 객체의 필드를 변경하지 못하는 상수화 키워드를 제공한다. (const)
그러나 이런건 실제로 매개변수를 다시 역으로 캐스팅하거나 실제 주소를 얻어와서 알마든지 객체나 매개변수의 값과 상태를 자유롭게 변경할 수 있다. 따라서 C++의 const키워드가 객체나 매개변수가 절대로 변경되지 않게 한다는 것은 잘못된 사실이다.
만약 CLR에서도 상수화를 지원한다면 상수화된 객체에 대해 쓰기 작업이 발생하지 않았는지 매 순간 검사해야 하며 이로 인해서 성능이 저하될 것이다.
이러한 이유로 CLR은 객체나 매개변수에 상수화를 지원하지 않는다.
'언어 > C#' 카테고리의 다른 글
C# 속성 (Property) - 2 (0) | 2020.12.27 |
---|---|
C# 속성 (Property) - 1. 속성, 매개변수 없는 속성 (0) | 2020.12.20 |
C# - @ (verbatim identifier) (2) | 2020.12.07 |
C# 메서드 - 3. 확장 메서드 (1) | 2020.11.28 |
C# 메서드 - 2. 연산자 오버로드 메서드 (0) | 2020.11.28 |