저 같은 경우는 C++으로 코딩을 주로 하다가 팀을 바꾸며 C#으로 코딩을 하게 됨으로서, 큰 learning curve없이 시작할 수 있었던 것 같습니다. 하지만 시간이 갈 수록, ‘이렇게 적는 것이 좋은 C# 코드일까?’ , ‘더 효과적인(effective) C# 방식으로 같은 로직을 짜는 방법은 무엇일까 ?’ 하는 생각들이 들기 시작했습니다.
C# 언어에 대한 공부보다는 이 언어를 좀 더 enterprise level product development 에서 더 잘 쓸 수 있는 방법에 대해서 배우고 싶어서, C# 사용법에 관한 리소스를 찾아보았습니다. 자바에서는 ‘Effective Java’라는 유명한 책이 바로 이러한 니즈를 채워준다고 어림풋이 알고 있어서, C#에서 Effective Java 같은 역할을 해주는 책이 무엇인지 조사해보던 결과 ‘More Effective C#’이라는 책을 접하게 되었습니다.
운이 좋게도 회사 온라인 도서관에서 Ebook으로 책을 무료로 볼 수 있을 뿐만 아니라 소유할 수 있어서, 이 책을 제 개인 공부시간에 조금씩 읽어보면서 배운 내용들과 기억하고픈 내용들을 노트 형식으로 정리해보기로 했습니다.
같은 저자가 쓴 ‘Effective C#’이라는 책도 있는데요, 이 책은 회사 도서관에 있는 버전으로는 C# 4.0버전을 바탕으로 설명이 들어가있어서, 이왕이면 제가 필요로한 C# 버전들을 커버해주는 ‘More Effective C#’으로 공부를 하기로 결정했습니다.
이번 포스트에서는 책의 첫 인트로 부분과 제 1장 1 섹션을 읽고 배웠던 부분들을 가볍게 정리해보았습니다.
C#의 어떻게 보면 굉장히 유니크한 feature라고도 볼 수 있습니다. Public Data Member, 즉 public field처럼 생겼지만, 실은 data member라 아니라 스페셜한 매소드인 accessors입니다.
예제:
class Customer
{
private string name; // private field
public string Name {
get { return name; }
set { name = "Totoro"; }
} // 여기서 `Name`이 property 입니다.
}
C#으로 작성을 하면서 늘 궁금했던 점이기도 했습니다. 도대체 왜 property라는 색다른 컨셉을 만들었을까에 대한 질문이었는데요, 즉 property가 주는 장점에 대해서 이해를 하고 싶었습니다. 이 책에서 장점들에 대해서 굉장히 잘 설명해 주고 있습니다.
(1) Data Encapsulation.
Name
이라는 public field를 해놓고, 모든 caller들이 이 Name
필드의 value를 set 할 수 있게 하면, 이 클래스의 implementation을 사용자들에게 노출시키는 경우로, data encapsulation 원칙을 지키지 않게 되는 굉장히 bad practice 라고 볼 수 있습니다. (2) Method 가지고 있는 이점들 사용 가능
Name
value의 getter , setter를 다음과 같은 additional logic을 가지도록 구현할 수 있습니다.class Customer
{
private string name;
public string Name {
get
{
if (String.IsNullOrEmpty(name))
{
return "default value";
}
return name;
}
set
{
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Name cannot be blank", nameof(Name));
name = value;
}
}
}
public class Customer
{
private object syncHandle = new object();
private string name;
public string Name
{
get {
lock (syncHandle)
return name;
}
set {
if (string.IsNullOrEmpty(value))
throw new ArgumentException( "Name cannot be blank", nameof(Name));
lock (syncHandle)
name = value;
}
}
....
}
public class Customer
{
public virtual string Name
{
get;
set;
}
}
public interface INameValuePair<T>
{
string Name { get; }
T Value { get; set; }
}
(3) 사용자들은 public fields를 사용하듯이 사용 가능.
class Program
{
static void Main(string[] args)
{
Customer customer = new Customer();
customer.Name = "torotoro";
Console.Write(customer.Name);
}
}
(4) .Net Framework에서는 public data members가 아닌 properties를 사용할 것이라는 가정을 바탕으로 만들어짐.
(5) Concise syntax로 표현 가능.
class Customer
{
public string Name { get; set; } //auto property
}
public class Location
{
private string locationName;
public Location(string name) => Name = name; // expression-bodied property
public string Name
{
get => locationName;
set => locationName = value;
} // expression-bodied property
}
(6) Indexer
class CustomerNames<T>
{
private List<T> names = new List<T>();
// indexer that allows client code to use [] notation
public T this[int i]
{
get => names[i];
set => names.Insert(i, value);
}
}
class Program
{
static void Main(string[] args)
{
//using indexer
var customerNames = new CustomerNames<string>();
customerNames[0] = "Totoro Peng";
Console.WriteLine(customerNames[0]); // print out 'Totoro Peng"
}
}
(7) 코드 변경을 해야 할 경우, 쉽게 변경 가능.
Name
field를 set하는 경우 모든 value가 다 가능했는데, 나중에는 empty string이거나 null인 value로 set을 할 경우 exception을 던지기로 하는 경우로 바꾸었다고 가정해보겠습니다. class Customer
{
public string Name { get; set; } //auto property
}
class Customer
{
private string name;
public string Name {
get {
if (String.IsNullOrEmpty(name))
{
return "default value";
}
return name;
}
set {
if (string.IsNullOrEmpty(value))
throw new ArgumentException("Name cannot be blank", nameof(Name));
name = value;
}
}
}
이러한 이유들 때문에, C#에서는 public data member 는 사용하지 말고 property를 사용하라는 룰이 있습니다.
하지만 property는 method이고 public data member는 데이터이기 때문에 property를 사용하였을 때, performance적으로는 문제가 없는지에 대한 궁금증이 생겼는데요, 이 책에서 이에 대한 답 또한 제시해 주었습니다.
JIT compiler를 사용하는 runtime environment (ex: JVM, .Net Framework)라면 JIT compiler가 property accessors같은 몇 개의 methods들은 inline하기 때문에 퍼포먼스에 차이가 없다고 말합니다. JIT compiler를 사용하지 않는 runtime environment 경우에도 퍼포먼스 차이가 굉장히 미미해서, 문제가 되지 않는다고 설명해주고 있습니다.
저도 회사에서 코드를 하면서 ‘굳이 왜 property?’ 라는 궁금증을 가진 적이 있었는데, 이 섹션을 읽으면서 이 질문에 대한 답을 조금 더 깊이있게 이해하고 공부할 수 있어서 재미있게 읽을 수 있었습니다.