[C# Class 예제]
namespace ProgrammingGuide { // Class definition. public class MyCustomClass { // Class members: // Property. public int Number { get; set; } // Method. public int Multiply(int num) { return num * Number; } // Instance Constructor. public MyCustomClass() { Number = 0; } } // Another class definition. This one contains // the Main method, the entry point for the program. class Program { static void Main(string[] args) { // Create an object of type MyCustomClass. MyCustomClass myClass = new MyCustomClass(); // Set the value of a public property. myClass.Number = 27; // Call a public method. int result = myClass.Multiply(4); } } }
[C# Class Features]
- C++과 다르게 struct는 값형식(복사시 값들이 복사 된다), class는 참조형식(복사시 참조만 복사 된다.)
: C#에서는 struct는 단지 값을 저장하기 위한 단순 용도로 사용해야 할 것 같음.
- 접근 제한자로 public, private, protected, internal protected internal 이 있음
- Constructor, destructor 정의는 대부분 유사하고 상속된 클래스들의 destructor들은 자동으로 자식부터 부모 순으로 호출 됨.
- getter, setter를 위해 손쉬운 property라는 방법을 제공함
- 단일 상속만 허용
: 하지만 인터페이스를 두개 이상 구현할 수 있음.
- 최상위 객체는 object 임
- 상속에서 virtual, override, sealed, new, abstract 등의 keyword들을 사용
[Access Modifiers, 접근 제한자]
일반적으로 public, private, protected만 봤었는데 어셈블리 레벨에서 접근을 제하하는 internal, protected internal 이 있음.
C# 한정자
|
정의
| |
---|---|---|
동일한 어셈블리의 다른 코드나 해당 어셈블리를 참조하는 다른 어셈블리의 코드에서 형식이나 멤버에 액세스할 수 있습니다.
| ||
동일한 클래스의 코드에서만 형식이나 멤버에 액세스할 수 있습니다.
| ||
동일한 클래스의 코드나 파생 클래스의 코드에서만 형식이나 멤버에 액세스할 수 있습니다.
| ||
동일한 어셈블리의 코드에서는 형식이나 멤버에 액세스할 수 있지만 다른 어셈블리의 코드에서는 액세스할 수 없습니다.
| ||
protected internal
|
동일한 어셈블리의 코드 또는 다른 어셈블리의 파생 클래스에서 형식이나 멤버에 액세스할 수 있습니다.
|
다음 예제에는 두 개의 파일 Assembly1.cs 및 Assembly1_a.cs가 포함되어 있습니다. 첫 번째 파일에는 내부 기본 클래스 BaseClass가 있습니다. 두 번째 파일에서 BaseClass를 인스턴스화하려고 시도하면 오류가 발생합니다.
// Assembly1.cs // Compile with: /target:library internal class BaseClass { public static int intM = 0; }
// Assembly1_a.cs // Compile with: /reference:Assembly1.dll class TestAccess { static void Main() { BaseClass myBase = new BaseClass(); // CS0122 } }
[Constructor(생성자), Destructor(소멸자)]
Constructor, 생성자
: 클래스 이름으로 표현, 반환 선언 없음.
- Default Constructor
: 아무 매개변수를 가지지 않는 constructor.
: 명시되지 않았을 경우 static constructor를 제외하고 컴파일러에서 자동 생성
- base
: 상속한 부모 class의 constructor를 명시적으로 호출하거나(1) 자신의 default constructor를 호출할 때 사용(2)
(1) 상속 관계에서 부모(Employee)의 constructor를 호출하는 예제
public class Manager : Employee { public Manager(int annualSalary) : base(annualSalary) { //Add further instructions here. } }
(2) 자신의 default constructor를 호출하는 예제
public class Employee { public int salary; public Employee(int annualSalary) { salary = annualSalary; } public Employee(int weeklySalary, int numberOfWeeks) { salary = weeklySalary * numberOfWeeks; } }
다음 문 중 하나를 사용하여 이 클래스를 만들 수 있습니다.
Employee e1 = new Employee(30000); Employee e2 = new Employee(500, 52);
생성자에서는 base 키워드를 사용하여 기본 클래스의 생성자를 호출할 수 있습니다.예를 들면 다음과 같습니다.
public class Manager : Employee { public Manager(int annualSalary) : base(annualSalary) { //Add further instructions here. } }
- this
: 자신이 가진 constructor를 호출할 때 사용 가능
public Employee(int weeklySalary, int numberOfWeeks) : this(weeklySalary * numberOfWeeks) { }
- Private Constructor
: Instance화가 필요없이 static member만 제공할 경우 사용하는 방법
class NLog { // Private Constructor: private NLog() { } public static double e = Math.E; //2.71828... }
- Static Constructor
: 정적 데이터를 초기화 하거나 생성자에서 수행하는 작업을 한번만 수행하기 위해서 사용
: 첫번째 인스턴스가 만들어지기 전이나 정적 멤버가 참조되기 전에 클래스를 초기화 하기 위해 자동으로 호출 됨.
class SimpleClass { // Static variable that must be initialized at run time. static readonly long baseline; // Static constructor is called at most one time, before any // instance constructor is invoked or member is accessed. static SimpleClass() { baseline = DateTime.Now.Ticks; } }
- Copy Constructor
: C#에서는 Copy Constructor를 제공하지 않지만 아래와 같이 만들 수 있음.
class Person { // Copy constructor. public Person(Person previousPerson) { Name = previousPerson.Name; Age = previousPerson.Age; } //// Alternate copy constructor calls the instance constructor. //public Person(Person previousPerson) // : this(previousPerson.Name, previousPerson.Age) //{ //} // Instance constructor. public Person(string name, int age) { Name = name; Age = age; } public int Age { get; set; } public string Name { get; set; } public string Details() { return Name + " is " + Age.ToString(); } }
Destructor, 소멸자
: ~와 클래스 이름으로 표현, 접근제한자/반환/매개변수 선언 없음.
: 클래스에서만 사용 가능하고 구조체에 정의할 수 없음.
: 하나의 소멸자만 정의 가능, 상속/오버로드 불가
: 상속관계에서 소멸자는 자식부터 부모순으로 호출 된다.
: 소멸자에서 자동으로 GC를 호출하므로 빈 소멸자는 정의 하지 않는 것이 추천 됨.
class Car { ~Car() // destructor { // cleanup statements... } }
소멸자는 개체의 기본 클래스에서 암시적으로 Finalize를 호출합니다. 따라서 위의 소멸자 코드는 암시적으로 다음과 같은 코드로 변환됩니다.
protected override void Finalize() { try { // Cleanup statements... } finally { base.Finalize(); } }
즉, Finalize 메서드는 최대 파생 인스턴스부터 최소 파생 인스턴스까지 상속 체인에 있는 모든 인스턴스에 대해 재귀적으로 호출됩니다.
참고 |
---|
빈 소멸자는 사용하지 않는 것이 좋습니다. 클래스에 소멸자가 포함된 경우 Finalize 큐에 엔트리가 만들어집니다. 소멸자가 호출되면 큐 처리를 위해 가비지 수집기가 호출됩니다. 그러므로 빈 소멸자는 성능 저하를 가져올 뿐입니다.
|
Object Initiailizer
: https://msdn.microsoft.com/ko-kr/library/bb397680.aspx
: 생성자를 명시적으로 호출하지 않고 선언적 방식으로 형식적 개체를 초기화 할 수 있음.
: 적절한 생성자를 호출하고 없다면 기본 생성자 호출 후 멤버 초기화
public class Program { public static void Main() { // Declare a StudentName by using the constructor that has two parameters. StudentName student1 = new StudentName("Craig", "Playstead"); // Make the same declaration by using an object initializer and sending // arguments for the first and last names. The default constructor is // invoked in processing this declaration, not the constructor that has // two parameters. StudentName student2 = new StudentName { FirstName = "Craig", LastName = "Playstead", }; // Declare a StudentName by using an object initializer and sending // an argument for only the ID property. No corresponding constructor is // necessary. Only the default constructor is used to process object // initializers. StudentName student3 = new StudentName { ID = 183 }; // Declare a StudentName by using an object initializer and sending // arguments for all three properties. No corresponding constructor is // defined in the class. StudentName student4 = new StudentName { FirstName = "Craig", LastName = "Playstead", ID = 116 }; System.Console.WriteLine(student1.ToString()); System.Console.WriteLine(student2.ToString()); System.Console.WriteLine(student3.ToString()); System.Console.WriteLine(student4.ToString()); } // Output: // Craig 0 // Craig 0 // 183 // Craig 116 } public class StudentName { // The default constructor has no parameters. The default constructor // is invoked in the processing of object initializers. // You can test this by changing the access modifier from public to // private. The declarations in Main that use object initializers will // fail. public StudentName() { } // The following constructor has parameters for two of the three // properties. public StudentName(string first, string last) { FirstName = first; LastName = last; } // Properties. public string FirstName { get; set; } public string LastName { get; set; } public int ID { get; set; } public override string ToString() { return FirstName + " " + ID; } }
[Property, Setter, Getter]
- get은 member 변수를 반환하고 set은 새 값을 할당하는데 사용
class TimePeriod
{
private double seconds;
public double Hours
{
get { return seconds / 3600; }
set { seconds = value * 3600; }
}
}
class Program
{
static void Main()
{
TimePeriod t = new TimePeriod();
// Assigning the Hours property causes the 'set' accessor to be called.
t.Hours = 24;
// Evaluating the Hours property causes the 'get' accessor to be called.
System.Console.WriteLine("Time in hours: " + t.Hours);
}
}
// Output: Time in hours: 24
class TimePeriod { private double seconds; public double Hours { get { return seconds / 3600; } set { seconds = value * 3600; } } } class Program { static void Main() { TimePeriod t = new TimePeriod(); // Assigning the Hours property causes the 'set' accessor to be called. t.Hours = 24; // Evaluating the Hours property causes the 'get' accessor to be called. System.Console.WriteLine("Time in hours: " + t.Hours); } } // Output: Time in hours: 24
- 각가 다른 접근 제한자를 가질 수 있음.
: get은 property의 기본 접근 제한자인 public 임
private string name = "Hello"; public string Name { get { return name; } protected set { name = value; } }
- value는 set 접근자에서 할당하는 값을 의미
- set을 구현하지 않는 속성은 읽기 전용
- set, get을 위한 별도 구현이 필요 없을 경우 아래 처럼 자동 구현 속성을 사용할 수 있음.
// This class is mutable. Its data can be modified from // outside the class. class Customer { // Auto-Impl Properties for trivial get and set public double TotalPurchases { get; set; } public string Name { get; set; } public int CustomerID { get; set; } // Constructor public Customer(double purchases, string name, int ID) { TotalPurchases = purchases; Name = name; CustomerID = ID; } // Methods public string GetContactInfo() {return "ContactInfo";} public string GetTransactionHistory() {return "History";} // .. Additional methods, events, etc. } class Program { static void Main() { // Intialize a new object. Customer cust1 = new Customer ( 4987.63, "Northwind",90108 ); //Modify a property cust1.TotalPurchases += 499.99; } }
[Inheritance(상속), Polymorphism(다형성)]
객체 지향 프로그래밍(Object-Oriented Programming)
: 개념이 너무 방대하여 자세한 내용은 적지 못하고 위 링크를 참고....
Inheritance, 상속
: 특정 클래스를 이용하여 새로운 클래스를 정의할 때 사용 되며 자식(파생) 클래스는 부모(기본) 클래스의 멤버를 접근 제한자에 따라서 접근할 수 있고 재정의 할 수 있다..
Polymorphism, 다형성
- Run-time에 자식 클래스는 다형성의 방법을 통해 부모 클래스의 형태로 처리 될 수 있고 이는 collection에 자식 클래스들을 넣고 처리할 경우 유용하다.
- 부모 클래스는 virtual method를 구현할 수 있고 이를 자식 클래스들에서는 재정의 하여 구현에서 자식 클래스의 종류에 상관없이 필요한 기능을 실행/사용 할 수 있다.
- 예제 설명
: Shape라는 부모 클래스를 만들고 Rectanble, Circle 및 Triangle과 같은 자식 클래스를 만듭니다.
: Shape 클래스에 Draw라는 가상 메서드를 제공하고, 각 자식 클래스들에서 재정의하여 자식 클래스가 나타내는 특정 도형을 그림.
: 부모 클래스의 Shape 형태의 List<Shape> 개체를 만들고 Shape를 상속한 Circle, Triangle 및 Rectangle을 추가할 수 있음.
: 화면을 업데이트하기 위해 foreach 루프를 사용하여 목록의 각 Shape 개체에서 가상 상속하고 있는 Draw 메서드를 호출합니다.
. 이 때 호출되는 목록의 형태는 Shape이더라도 호출되는 런타임 형식은 각 파생 클래스에서 재정의한 Draw 메서드입니다.
public class Shape { // A few example members public int X { get; private set; } public int Y { get; private set; } public int Height { get; set; } public int Width { get; set; } // Virtual method public virtual void Draw() { Console.WriteLine("Performing base class drawing tasks"); } } class Circle : Shape { public override void Draw() { // Code to draw a circle... Console.WriteLine("Drawing a circle"); base.Draw(); } } class Rectangle : Shape { public override void Draw() { // Code to draw a rectangle... Console.WriteLine("Drawing a rectangle"); base.Draw(); } } class Triangle : Shape { public override void Draw() { // Code to draw a triangle... Console.WriteLine("Drawing a triangle"); base.Draw(); } } class Program { static void Main(string[] args) { // Polymorphism at work #1: a Rectangle, Triangle and Circle // can all be used whereever a Shape is expected. No cast is // required because an implicit conversion exists from a derived // class to its base class. System.Collections.Generic.List<Shape> shapes = new System.Collections.Generic.List<Shape>(); shapes.Add(new Rectangle()); shapes.Add(new Triangle()); shapes.Add(new Circle()); // Polymorphism at work #2: the virtual method Draw is // invoked on each of the derived classes, not the base class. foreach (Shape s in shapes) { s.Draw(); } // Keep the console open in debug mode. Console.WriteLine("Press any key to exit."); Console.ReadKey(); } } /* Output: Drawing a rectangle Performing base class drawing tasks Drawing a triangle Performing base class drawing tasks Drawing a circle Performing base class drawing tasks */
- virtual, override
: 부모 클래스는 자식 클래스가 재정의 할 수 있도록 virtual로 명시함.
. virtual method가 호출 되면 개체의 run-time 형식이 검사 됨.
. virtual 한정자는 static, abstract, private, override 한장자와 함께 사용 불가
: 자식 클래스는 Rum-time시 가상 호출이 될 수 있도록 override를 명시함.
. override로는 virtual, abstract, override 속성의 method를 재정의 가능하지만 non virtual, static 속성의 method는 재정의 불가
. virtual method의 접근 제한자를 변경 할 수 없음.
public class BaseClass { public virtual void DoWork() { } public virtual int WorkProperty { get { return 0; } } } public class DerivedClass : BaseClass { public override void DoWork() { } public override int WorkProperty { get { return 0; } } }
: 자식 클래스가 부모 클래스의 instance로 사용 되는 경우에도 override된 method가 호출 됨.
DerivedClass B = new DerivedClass(); B.DoWork(); // Calls the new method. BaseClass A = (BaseClass)B; A.DoWork(); // Also calls the new method.
- new
: 상속된 부모 클래스의 member를 숨길 경우 사용
public class BaseClass { public void DoWork() { WorkField++; } public int WorkField; public int WorkProperty { get { return 0; } } } public class DerivedClass : BaseClass { public new void DoWork() { WorkField++; } public new int WorkField; public new int WorkProperty { get { return 0; } } }
: 부모 클래스로 캐스팅하여 부모 클래스의 method를 호출 가능
DerivedClass B = new DerivedClass(); B.DoWork(); // Calls the new method. BaseClass A = (BaseClass)B; A.DoWork(); // Calls the old method.
- sealed
: 상속된 클래스간에 가상 상속은 무제한으로 가능함.
: 자식 클래스에서 더이상의 가상 상속을 방지하기 위해서는 sealed를 선언함.
public class A { public virtual void DoWork() { } } public class B : A { public override void DoWork() { } }
public class C : B { public sealed override void DoWork() { } }
- base
: 자식 클래스에서 부모 클래스의 method나 속성에 접근할 때 사용
public class Base { public virtual void DoWork() {/*...*/ } } public class Derived : Base { public override void DoWork() { //Perform Derived's work here //... // Call DoWork on base class base.DoWork(); } }
- abstract
: abstract 클래스에 포함된 abstract 멤버는 파생되는 자식 클래스에서 구현되어야 함.
: abstract 클래스는 인스턴스화 될 수 없음.
: abstract 클래스는 seal 한정자와 함께 사용 불가 (의미가 상충)
: abstract method는 abstract class에서만 허용
: virtual method와 abstract method의 차이점
. static 속성에는 abstract 한정자 사용 불가
. 상속된 abstract 속성은 override 한정자를 사용하여 자식 클래스에서 재정의 가능
abstract class ShapesClass { abstract public int Area(); } class Square : ShapesClass { int side = 0; public Square(int n) { side = n; } // Area method is required to avoid // a compile-time error. public override int Area() { return side * side; } static void Main() { Square sq = new Square(12); Console.WriteLine("Area of the square = {0}", sq.Area()); }
}
- is
: 객체가 주어진 형식에 맞는지 확인할 수 있음.
: Run-time 시 확인 가능하고 true/false 리턴
: reference conversions, boxing/unboxing conversions에만 허용됨.
class Class1 {} class Class2 {} class Class3 : Class2 { } class IsTest { static void Test(object o) { Class1 a; Class2 b; if (o is Class1) { Console.WriteLine("o is Class1"); a = (Class1)o; // Do something with "a." } else if (o is Class2) { Console.WriteLine("o is Class2"); b = (Class2)o; // Do something with "b." } else { Console.WriteLine("o is neither Class1 nor Class2."); } } static void Main() { Class1 c1 = new Class1(); Class2 c2 = new Class2(); Class3 c3 = new Class3(); Test(c1); Test(c2); Test(c3); Test("a string"); } } /* Output: o is Class1 o is Class2 o is Class2 o is neither Class1 nor Class2. */
- as
: 변환 가능한 reference 형식으로 변환하거나 nullable 형식으로 변환함.
: as는 cast와 유사하며 변환이 불가할 경우 exception을 발생하지 않고 null을 리턴함.
class ClassA { } class ClassB { } class MainClass { static void Main() { object[] objArray = new object[6]; objArray[0] = new ClassA(); objArray[1] = new ClassB(); objArray[2] = "hello"; objArray[3] = 123; objArray[4] = 123.4; objArray[5] = null; for (int i = 0; i < objArray.Length; ++i) { string s = objArray[i] as string; Console.Write("{0}:", i); if (s != null) { Console.WriteLine("'" + s + "'"); } else { Console.WriteLine("not a string"); } } } } /* Output: 0:not a string 1:not a string 2:'hello' 3:not a string 4:not a string 5:not a string */
댓글 없음:
댓글 쓰기