implicit
연산자를 오버라이딩하여 클래스간 형식 변환을 지원할 수 있다. 이 연산자를 이용해서 명시적, 암시적 형변환 모두 가능하다.
static public operator 형변환결과 클래스(형변환 전 클래스){
// 형변환 절차
}
아래 코드는 implicit
연산자로 Gorani, Cat클래스 간 명시적, 암시적 형변환을 수행한다.
namespace Program
{
public class Animal
{
public int CryNumber { get; init; }
public Animal(int CryNumber)
{
this.CryNumber = CryNumber;
}
}
public class Gorani : Animal
{
public Gorani(int CryNumber) : base(CryNumber) { }
private string sound = "queeeeaaeak!";
public override string ToString()
{
string s = sound;
for (int i = 0; i < CryNumber; i++)
s += sound;
return "[Gorani] " + s;
}
static public implicit operator Cat(Gorani gorani)
{
return new Cat(gorani.CryNumber);
}
}
public class Cat : Animal
{
public Cat(int CryNumber) : base(CryNumber) { }
private string sound = "yaong!";
public override string ToString()
{
string s = sound;
for (int i = 1; i < CryNumber; i++)
s += sound;
return "[Cat] " + s;
}
static public implicit operator Gorani (Cat cat)
{
return new Gorani(cat.CryNumber);
}
}
class Program
{
public static void Main(string[] args)
{
Gorani gorani = new Gorani(1);
Cat cat = new Cat(3);
Console.WriteLine(gorani.ToString()); // [Gorani] queeeeaaeak!
Console.WriteLine(cat.ToString()); // [Cat] yaong!yaong!yaong!
// implicit연산자로 암시적 변환(implicit)
Gorani catTOgorani = cat;
Cat goraniTOcat = gorani;
Console.WriteLine(catTOgorani.ToString()); //[Gorani] queeeeaaeak!queeeeaaeak!queeeeaaeak!
Console.WriteLine(goraniTOcat.ToString()); //[Cat] yaong!
// implicit연산자로 명시적 변환(explict)도 가능
catTOgorani = (Gorani)cat;
goraniTOcat = (Cat)gorani;
Console.WriteLine(catTOgorani.ToString()); //[Gorani] queeeeaaeak!queeeeaaeak!queeeeaaeak!queeeeaaeak!
Console.WriteLine(goraniTOcat.ToString()); //[Cat] yaong!
}
}
}
explicit
연산자를 오버라이딩하여 클래스간 명시적 형변환을 구현할 수 있다. 위 implicit
연산자 코드에서 implicit
키워드를 explicit
으로 바꾼 것이다.
이 경우 클래스간 명시적 형변환만 가능하다. 암시적 형변환을 시도하면 컴파일 에러가 발생한다.
CS0266 암시적으로 'Program.Cat' 형식을 'Program.Gorani' 형식으로 변환할 수 없습니다. 명시적 변환이 있습니다. 캐스트가 있는지 확인하세요.
namespace Program
{
public class Animal
{
public int CryNumber { get; init; }
public Animal(int CryNumber)
{
this.CryNumber = CryNumber;
}
}
public class Gorani : Animal
{
public Gorani(int CryNumber) : base(CryNumber) { }
private string sound = "queeeeaaeak!";
public override string ToString()
{
string s = sound;
for (int i = 0; i < CryNumber; i++)
s += sound;
return "[Gorani] " + s;
}
static public explicit operator Cat(Gorani gorani)
{
return new Cat(gorani.CryNumber);
}
}
public class Cat : Animal
{
public Cat(int CryNumber) : base(CryNumber) { }
private string sound = "yaong!";
public override string ToString()
{
string s = sound;
for (int i = 1; i < CryNumber; i++)
s += sound;
return "[Cat] " + s;
}
static public explicit operator Gorani (Cat cat)
{
return new Gorani(cat.CryNumber);
}
}
class Program
{
public static void Main(string[] args)
{
Gorani gorani = new Gorani(1);
Cat cat = new Cat(3);
Console.WriteLine(gorani.ToString()); // [Gorani] queeeeaaeak!queeeeaaeak!
Console.WriteLine(cat.ToString()); // [Cat] yaong!yaong!yaong!
// explicit연산자로는 오직 명시적 변환(explict)만 가능
Gorani catTOgorani = (Gorani)cat;
Cat goraniTOcat = (Cat)gorani;
Console.WriteLine(catTOgorani.ToString()); //[Gorani] queeeeaaeak!queeeeaaeak!queeeeaaeak!queeeeaaeak!
Console.WriteLine(goraniTOcat.ToString()); //[Cat] yaong!
// explicit연산자로 암시적 변환(implicit)
// -----> 컴파일에러
catTOgorani = cat;
goraniTOcat = gorani;
}
}
}
readonly
키워드를 이용하여 어떤 필드를 읽기전용 필드로 지정할 수 있다.
생성자에서만 초기화 가능하다.
다른 방법(아래 코드에서는 다른 메소드)으로 값을 넣으려고 하면 컴파일에러가 발생한다.
CS0191 읽기 전용 필드에는 할당할 수 없습니다. 단, 필드가 정의된 형식의 생성자 또는 초기값 전용 setter나 변수 이니셜라이저에서는 예외입니다.
class Something
{
private readonly int a;
private readonly string b;
public Something(int a, string b)
{
this.a = a;
this.b = b;
}
public void updateValue(int a, string b)
{
this.a = a; // 컴파일에러 CS0191
this.b = b; // 컴파일에러 CS0191
}
}
어떤 클래스를 클래스 내에 중첩시켜, 밖에 보이지 않도록 할 수 있다.
중첩된 클래스는 자신이 소속된 클래스 멤버가 private일지라도 접근 가능하다.
아래 코드에서는 클래스 InnerClass가 OuterClass 내부에 중첩되어있다.
namespace Program
{
class OuterClass
{
List<InnerClass> list = new List<InnerClass>();
public void Add(int value)
{
InnerClass inner = new InnerClass();
inner.AddItem(this, value);
}
public void Get()
{
foreach(InnerClass item in list)
{
Console.Write(item.GetItem());
Console.Write(' ');
}
}
class InnerClass
{
int value;
public void AddItem(OuterClass outer, int value)
{
this.value = value;
outer.list.Add(this);
}
public int GetItem()
{
return value;
}
}
}
class Program
{
public static void Main(string[] args)
{
OuterClass a = new OuterClass();
a.Add(1);
a.Add(2);
a.Add(3);
a.Get(); // 1 2 3
}
}
}
partial
키워드를 사용해 키워드를 여러번에 나눠 구현할 수 있다.
코드 내 다른 블록으로 나눌 수도 있고, 다른 파일에 나눌 수도 있다.
분할 클래스로 정의되어있더라도, 컴파일 될 때 하나의 클래스로 묶여진다.
클래스의 구현이 길어지는 경우 유용하게 사용 할 수 있다.
namespace Program
{
partial class Something
{
public Something(int a)
{
this.a = a;
Console.WriteLine("initialized "+a.ToString());
}
}
partial class Something
{
int a;
}
class Program
{
public static void Main(string[] args)
{
Something test = new Something(5); // initialized 5
}
}
}
기존 클래스의 기능을 확장하는 기법
상속과는 다르다.
아래와 같은 형태로 확장을 정의한다.
public static class 클래스명
{
public static 반환형 메소드명(this 대상형식 식별자, 매개변수){
// 메소드 수행내용
}
}
아래 코드는 int자료형에 대해, 입력된 매개변수와의 비교 결과를 반환하는 확장메소드이다.
namespace Program
{
public static class Something
{
public static string CompareWith(this int a, int b)
{
if (a > b)
{
return "bigger";
}
else if (a < b)
{
return "smaller";
}
else
{
return "same";
}
}
}
class Program
{
public static void Main(string[] args)
{
Console.WriteLine(3.CompareWith(53)); // smaller
Console.WriteLine(3.CompareWith(-53)); // bigger
Console.WriteLine(3.CompareWith(3)); // same
}
}
}