C#/문법

최근 수정 시각: (5년 전)
목차
1. 개요2. 기본3. 편집 지침4. 클래스 (Class)
4.1. this, base
4.1.1. this4.1.2. base
4.2. 생성자 (Consturctor)
4.2.1. 일반 생성자 (public/private/internal/protected)4.2.2. static 생성자4.2.3. const, readonly
4.3. 속성 (Property)
4.3.1. 속성 정의
4.4. 추상 클래스 (Abstract Class)4.5. 상속 / 구현 (Inherits / Implementation)4.6. unsafe4.7. checked / unchecked
5. 인터페이스 (Interface)6. 자료형 (Type)
6.1. var6.2. 값 형식
6.2.1. 부울(bool)6.2.2. 숫자(byte, short ...)
6.2.2.1. BigInteger (System.Numerics)6.2.2.2. 문자(char)
6.2.3. 날짜/시간(DateTime)
6.3. 참조 형식
6.3.1. object6.3.2. 문자열(string)
6.4. 리터럴 값 형식6.5. 제네릭 형식6.6. 암시적 형식, 무명 형식, nullable 값 형식
7. 함수 (Function)
7.1. 입출력 함수
7.1.1. 콘솔 입출력

1. 개요 [편집]

C#의 문법을 설명하는 문서이다.
C#C++Java 문법과 유사하므로 C++/문법 문서와 비교하여 참조하는 것이 좋다.

2. 기본 [편집]

C# 코드는 보통 다음과 같이 구성한다.
using System; 
// 여기에 사용할 .NET 네임스페이스를 정의한다.
// 예: 제네릭 컬렉션 -> using System.Collections.Generic;
//     파일 입출력   -> using System.IO;
//     정규표현식    -> using System.Text.RegularExpressions;

namespace CSharpExample
{
    // 모든 C# 클래스는 한 네임스페이스 안에 선언한다.
    class Program
    {
        // 여기에 멤버 변수, 프로퍼티, 메소드 등을 선언한다.
        static void Main(string[] args)
        {
            //여기에 돌아갈 코드를 작성한다.
        }
    }
}


C#Java와는 달리 메인 클래스 이름과 파일 이름이 반드시 같을 필요는 없지만 혼동을 피하기 위해 가급적 동일하게 지정할 것을 추천한다.

3. 편집 지침 [편집]


소스 코드로 예시를 들 때
{{{#!syntax csharp (소스코드) }}}
문법을 활용하여 소스코드를 써 주시기 바랍니다.

예시:
using System;

namespace Namu
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("hello namu!");
        }
    }
}


4. 클래스 (Class) [편집]

C# 클래스 기본 예제
namespace CSharpExample
{
    public class Person
    {
        public const int EyeCount = 2;

        private string name;

        public int Age { get; set; } = 0;

        public string Name { get => name; set => name = value; }

        public void Run()
        {
            // 달리는 동작
        }

        public override string ToString()
        {
            return quot;{name} ({Age})";
        }

        public Person(string name, int age)
        {
            this.name = name;
            Age = age;
        }
    }
}


C# 클래스의 기능은 Java와 거의 동일하다. 다만 C# 클래스만의 특징을 따로 서술하면 다음과 같다.
  • 분할 클래스 : Java 클래스는 모든 멤버들을 한 파일에 작성해야 한다. 하지만 C# 클래스는 아래와 같이 partial 지시자를 사용하여 여러 파일에 나누어 작성할 수 있다. 이렇게 하면 나누어 놓은 클래스들이 하나의 클래스로 취급된다. [1]
    // ...
     public partial class PartialClass
     {
         public PartialClass()
         {
             // 생성자
         }
    
         public void Method1()
         {
             // Method1
         }
     }
    
     public partial class PartialClass
     {
         public void Method2()
         {
             // Method2
         }
    
         public void Method3()
         {
             // Method3
         }
     }
     // ...

4.1. this, base [편집]

4.1.1. this [편집]

this 키워드는 정의된 클래스로 생성된 인스턴스 자기 자신을 나타내는 키워드이다.
(확장 메서드에서의 첫번째 매개 변수 한정자로도 사용되지만 그 부분은 뒤에 다시 다루겠다.)

같은 이름의 변수가 메서드 스코프와 클래스 단위 스코프에 정의되어 있을 경우 this 키워드를 통해서 접근할 수 있다.
public class Employee
{
    private string company;
    private string name;

    public Employee(string name, string company)
    {
        this.name = name;
        this.company = company;
    }
}

위의 예제의 경우 클래스 단위에서 company와 name이 정의되어 있고, consturctor[2]에서 다시 name과 company를 파라미터로 받고 있다.

이 경우 단순히 name으로만 접근하려고 name = name을 하더라도 생성자의 name 파라미터에만 접근이 되기 때문에 원하는 작업을 수행할 수 없다.

이 경우에 사용하는 것이 this 키워드이다. this.name = name을 하게 되면 클래스의 필드에 생성자의 name을 대입하는 정상적인 코드를 만들 수 있다.

4.1.2. base [편집]

base 키워드의 경우 본인이 상속 받고 있는 기본 클래스의 멤버에 접근하는데에 사용되는 키워드이다.

따라서, 상속을 허용하지 않는 static 클래스에서 base 키워드를 사용할 경우, 오류가 발생한다.

만약 상속 받은 파생 클래스에서 재정의한 동작이나 필드가 있을 경우에도 base 키워드를 통해서 기본 클래스의 멤버에 접근할 수 있다.
public class Person
{
    protected string telephone = "12-5555-6666";
    protected string name = "홍길동";

    public virtual void GetInfo()
    {
        Console.WriteLine("Name: {0}", name);
        Console.WriteLine("Tel: {0}", telephone);
    }
}
class Employee : Person
{
    public string id = "ABC567EFG";
    public override void GetInfo()
    {
        // 본인이 상속 받은 클래스에 있는 GetInfo 메소드를 호출
        base.GetInfo();
        Console.WriteLine("Employee ID: {0}", id);
    }
}

class TestClass
{
    static void Main()
    {
        Employee E = new Employee();
        E.GetInfo();
    }
}

4.2. 생성자 (Consturctor) [편집]

생성자는 class나 struct가 생성될때 호출되는 메소드이다. 선언 방식에 따라서 다양한 접근자를 가진 생성자를 정의할 수 있다.

4.2.1. 일반 생성자 (public/private/internal/protected) [편집]

static 생성자를 제외한 나머지 생성자는 new 클래스이름() 으로 접근할 수 있다.

단, 생성자 앞에 private이나 internal을 붙일 경우 필드 정의와 마찬가지로 엑세스가 제한되게 된다.

C# 9.0부터는 객체의 타입을 미리 알 수 있는 경우, new 클래스이름() 대신 new () 로만 접근할 수 있다.

public class TestClass
{
    private TestClass()
    {
        Console.WriteLine("Private Constructor");
    }

    internal TestClass(string message)
    {
        Console.WriteLine("Internal Constructor");
        Console.WriteLine("Message: " + message);
    }
    
    protected TestClass(int count)
    {
        Console.WriteLine("Protected Constructor");
        Console.WriteLine("Count: " + count);
    }

    public TestClass(string message, int count)
    {
        Console.WriteLine("Public Constructor");
        Console.WriteLine("Message: " + message);
        Console.WriteLine("Count: " + count);
    }
}

public class Program
{
    public static void Main()
    {
        var cls1 = new TestClass(); // Private 접근이 안되므로 오류
        var cls2 = new TestClass("test"); // 같은 어셈블리가 아닐 경우 오류
        var cls3 = new TestClass(5); // TestClass 또는 TestClass에서 상속받은 클래스가 아닐 경우 오류
        var cls4 = new TestClass("test", 5); // 모든 곳에서 접근 가능
        TestClass cls5 = new("test", 5); // cls5의 타입을 알 수 있으므로 클래스 이름 생략 가능 (C# 9.0부터 지원)
    }
}

만약 생성자에서 중복을 없애기 위해서 본인의 생성자 혹은 본인이 상속받고 있는 부모 클래스의 생성자에 접근하기 위해서는 다음과 같다.
public class BaseClass
{
    protected BaseClass()
    {
        Console.WriteLine("[Base Protected Constructor]");
    }
}

public class TestClass : BaseClass
{
    private TestClass() : base()
    {
        Console.WriteLine("[Private Constructor]");
    }

    internal TestClass(string message) : this()
    {
        Console.WriteLine("[Internal Constructor]");
        Console.WriteLine("Message: " + message);
    }
    
    public TestClass(string message, int count) : this(message)
    {
        Console.WriteLine("[Public Constructor]");
        Console.WriteLine("Count: " + count);
    }
}

public class Program
{
    public static void Main()
    {
        var cls = new TestClass("test", 5);
        // 출력:
        // [Base Protected Constructor]
        // [Private Constructor]
        // [Internal Constructor]
        // Message: test
        // [Public Constructor]
        // Count: 5
    }
}

4.2.2. static 생성자 [편집]

static 생성자는 일반적인 생성자완 달리 해당 클래스에 처음 접근할때 호출되는 메서드다.

static 생성자를 정의하기 위해서는 다른 엑세스 한정자 (public 등)을 사용하지 않는다.

또한, static 생성자는 직접 호출이 불가능하며 상속이나 오버로드도 허용하지 않는다.
class TestClass
{
    static TestClass()
    {
        // 정적 생성자
    }
}

직접 호출이 불가능하다고는 했지만, 일반적으로 해당 클래스에 처음 접근할때 호출 된다고 했으므로, 아무런 작업을 하지 않는 메서드를 만들어서 호출해주면 static 생성자도 같이 호출된다. (정확히는 먼저 호출된다.)
class TestClass
{
    static TestClass()
    {
        // 정적 생성자
    }
    
    public static void Init()
    {
        // Fake Method
    }
}

// TestClass.Init();

4.2.3. const, readonly [편집]

const로 선언된 변수는 반드시 값을 초기화시켜야 하고 초기화된 값을 변경할 수 없다. 또한 자동으로 static 변수가 되기에 public이면 다른 클래스에서 접근이 가능하다. const와 static를 동시에 쓰면 컴파일 오류가 난다.

readonly의 경우 const와 다르게 값을 초기화해야할 필요가 없고 생성자에서 값을 변경할 수 있으며 자동으로 static 변수가 되지 않는다. 따라서 다른 클래스에서 사용하려면 static와 함께 선언해야 한다.

class TestClass
{
    static TestClass()
    {
        // 생성자에서 readonly 변수 값 변경이 가능하다.
        TestReadOnly = 1;
    }

    // 한번 선언된 값은 변경이 불가능하고 자동으로 static 변수가 된다.
    public const int TestConst = 4;

    // 초기화가 필요 없으며, 생성자에서 값을 변경할 수 있으며, 자동으로 static 변수가 되지 않는다.
    public readonly int TestReadOnly;
}

class Program
{
    static void Main(string[] args)
    {
        // const 선언 시 자동으로 static 변수가 되기 때문에 public이면 다른 클래스에서 접근할 수 있다.
        Console.WriteLine(TestClass.TestConst);
        // const 변수는 변경이 불가능하기 때문에 오류가 난다.
        TestClass.TestConst = 5;
    }
}

4.3. 속성 (Property) [편집]

속성을 일반적으로 Java에서 구현하기 위해서는 클래스 내에 private 필드를 만들어두고 메서드 이름 앞에 get이나 set을 붙여서 사용했다.

하지만, C#에서는 문법 단에서 해당 기능을 구현할 수 있도록 되어 있다.

4.3.1. 속성 정의 [편집]

일반적으로 C#에서는 속성을 다음과 같이 정의한다.
public class Person
{
    public string Name { get; set; }
}

이 구문을 Java 스타일로 바꿔보자면 다음과 같다.
public class Person
{
    // Java Style
    private string _name;
    
    public void setName(string name)
    {
        _name = name;
    }

    public string getName()
    {
        return _name;
    }
}

만약 속성을 정의하면서 초기값을 지정하고 싶다면 다음과 같이 지정한다.
public class Person
{
    public string Name { get; set; } = string.Empty;
}

만약 이름을 설정하거나 가져올때 추가로 구현하고 싶은 로직이 있는 경우 다음과 같이 정의한다.
public class Person
{
    private string _name;
    public string Name
    {
        get
        {
            // logic
            return _name;
        }
        set
        {
            // logic
            _name = value;
        }
    }
}

하지만 단일 식일 경우에는 다음과 같이 쓸수도 있다.
public class Person
{
    private string _name;
    public string Name
    {
        get => _name;
        set => _name = value;
    }
}

set 부분을 클래스 내에서만 접근하고 싶다면 다음과 같이 정의한다.
public class Person
{
    public string Name { get; private set; }
}

4.4. 추상 클래스 (Abstract Class) [편집]

4.5. 상속 / 구현 (Inherits / Implementation) [편집]

상속 또는 구현을 할 때는 다음과 같이 클래스 이름 뒤에 :(콜론)을 붙이고 상속받을 클래스 이름 또는 구현할 인터페이스 이름을 적는다. 여러게 적을 때에는 ,로 구분한다
public class DerivedClass : BaseClass, ISomeInterface


상속의 경우 Java와 마찬가지로 클래스의 종류에 상관없이 단 한 개의 클래스만 상속받을 수 있다.

4.6. unsafe [편집]

C#는 쓰레기 수집기를 사용하기 때문에 메모리 관리가 필요없으며 포인터 변수도 지원하지 않는다. 하지만 Windows API를 사용할 때나 기타 상황 때 포인터 변수를 사용해야 한다면 unsafe 키워드를 이용하면 된다.

public class Program
{
    static void Main(string[] args)
    {
        unsafe
        {
            // 포인터 변수 선언, 이 코드는 unsafe 키워드 안에서 실행해야 한다.
            int* Point1 = 500;
            int* Point2 = 1000;

            // 포인터 메서드 호출
            PointFun(&Point1, &Point2);

            // 원래라면 Point1 : 500, Point2 : 1000 으로 표시해야 하지만 포인터 변수 값을 변경하는 메서드를 호출했기 때문에 Point1 : 5, Point2 : 10 으로 표시된다.
            Console.WriteLine("Point1 : {0}, Point2 : {0}", Point1, Point2);
        }
    }

    // unsafe는 메서드 생성자에 넣을 수 있다. 그러면 해당 메서드는 포인터 변수를 다롤수 있다.
    static unsafe void PointFun(int* point, int* point2)
    {
        // point와 point2 값을 변경한다. point와 point2는 Point1, Point2의 주소를 가지고 있기 때문에 Point1, Point2 값이 변경된다.
        *point = 5;
        *point2 = 10;
    }
}


다만 unsafe를 사용하기 위해서는 설정 - 빌드에서 '안전하지 않은 코드 허용'을 체크해야 한다. 안그러면 컴파일 시 오류가 난다.

비주얼 스튜디오를 사용하지 않고 프로그래밍한다면 컴파일러에 /unsafe 매개 변수를 추가하는 것으로 unsafe 코드를 컴파일할 수 있다.

4.7. checked / unchecked [편집]

checked 키워드는 컴파일 과정에서 상수 변수들의 값 연산이나 변환에서 일어날 수 있는 오버플로우, 언더플로우가 발생한 가능성이 있으면 오류를 낸다. unchecked 키워드는 컴파일 과정에서 어떤 코드에서 오버플로우 또는 언더플로우가 일어날 가능성이 있더라도 이를 무시하는 키워드다.

.NET에서 오버플로우 또는 언더플로우가 발생하더라도 오류 메시지를 발생하지 않으며, 컴파일 과정에서 어느 코드에서 오버플로우 또는 언더플로우가 발생할 가능성이 있으면 컴파일 오류가 난다. 즉 오버플로우 또는 언더플로우 발생 가능성이 있는 코드를 컴파일에서 가려내는 식이다.

기본적으로 unchecked 키워드 안의 코드 외의 코드들은 오버플로우/언더플로우 감시 대상이 된다.

해당 키워드 사용은 다음과 같다.

// 오버플로우/언더플로우 감시 사용
checked
{

}

// 오버플로우/언더플로우 감시 사용 안함
unchecked
{

}

5. 인터페이스 (Interface) [편집]


구현이 없는, 메서드 선언만을 정의 해 놓은 자료형이다.
엄연히 자료형의 일종이지만, 생성자를 가질 수 없다. 즉, 인터페이스만으로 인스턴스를 만들 수 없다.

생성 방법은 클래스와 비슷한데, 필드를 가질 수 없으며 메서드는 구현 없이 괄호 닫고 세미콜론을 넣는다.

public class Person
    {
        public uint Balance { get; set; }
    }
    //인터페이스 선언
    public interface IPurchasable
    {
        public uint Cost { get; /*프로퍼티의 get, set도 메서드이다.*/}

        public void Purchase(Person person);
    }

    public interface IBrighter
    {
        public void Brighten();
    }
    //구현
    public class FlashLight : IPurchasable, IBrighter
    {
        public uint Cost => 300;

        public void Purchase(Person person)
        {
            //구입 기능 구현
        }

        public void Brighten()
        {
            //불 밝히기 기능 구현
        }
    }

    public class Program
    {
        public static void Main(string[] args)
        {
            var person = new Person() { Balance = int.Parse(args[0]) };
            //변수로 선언할 경우 그 인터페이스를 구현한 모든 클래스/구조체를 변수에 대입 할 수 있다.
            IBrighter flashLight = new FlashLight();
            flashLight.Purchase(person);
        }
    }

6. 자료형 (Type) [편집]

닷넷 기반의 여러 언어에서 공통으로 사용되는 자료형을 CTS(Common Type System)라고 부른다. System.Object를 상속 받아 구현된다. 스택 영역에 데이터를 저장한다.

6.1. var [편집]

MSDN 공식 문서

어떤 자리에 들어올 자료형을 컴파일러도 결정할 수 있도록 명확할 때, 자료형을 길게 다 쓰지 않고, var이라는 키워드로 줄일 수 있다.
자료형 문단에 들어있지만 엄밀히 말하면 var자체가 자료형인게 아니다.

C# 3.0 부터 추가된 스펙이며, 메서드안에서 선언된 변수에 암시적 타입으로 var를 사용할 수 있다. 메서드안에서만 선언이 가능하므로, 전역 변수에서는 아무리 초기값을 지정해준다고 하더라도 var를 사용할 수 없다. 암시적 형식 지역 변수는 직접 선언한 것과 같은 형식이지만 컴파일 시점에 컴파일러가 값에 따라 형식을 결정한다.

즉, 다음의 선언들은 형식 및 동작이 같다.

var i = 10; // 암시적 형식 선언
int i = 10; // 명시적 형식 선언

var s = "namu"; // 암시적 형식 선언
string s = "namu"; // 명시적 형식 선언

6.2. 값 형식 [편집]

값 형식은 참조 형식과 반대되는 형식의 범주이다. 참조 형식이 아니므로, 기본적으로는 데이터가 수정되지 않고 복사된다.

자세한 정보는 MSDN 공식 문서를 참조하면 된다.

6.2.1. 부울(bool) [편집]


bool 형식은 System.Boolean의 별칭이다. 기본 값으로 false를 가지고 있다.

기본적으로 bool 값은 true와 false 두개의 상태를 가지고 있지만, Nullable을 사용하게 되면 아래와 같이 세개의 상태를 나타낼 수 있다.
// bool
bool check = true;
Console.WriteLine(check ? "Checked" : "Not checked");  // 출력: Checked
Console.WriteLine(false ? "Checked" : "Not checked");  // 출력: Not checked

// Nullable bool
bool? b1 = true;
bool? b2 = false;
bool? b3 = null;

Console.WriteLine(b1); // 출력: true
Console.WriteLine(b2); // 출력: false
Console.WriteLine(b3); // 출력: 

Console.WriteLine(b1.HasValue); // 출력: true
Console.WriteLine(b2.HasValue); // 출력: true
Console.WriteLine(b3.HasValue); // 출력: false

// Nullable in Condition
if (b1.HasValue)
{
    if (b1)
    {
        // b1은 값이 있으며, true이다.
    }
    else
    {
        // b1은 값이 있으며, false이다.
    }
}
else
{
    // b1은 값이 없다 (null)
}

6.2.2. 숫자(byte, short ...) [편집]

숫자 형식의 종류는 다음과 같다.
줄임말
형식
표시 숫자
범위
크기
sbyte
System.SByte
부호 있는 8비트 정수
-128 ~ 127
8Bit
byte
System.Byte
부호 없는 8비트 정수
0 ~ 255
8Bit
short
System.Int16
부호 있는 16비트 정수
-32,768 ~ 32,767
16Bit
ushort
System.UInt16
부호 없는 16비트 정수
0 ~ 65,535
16Bit
int
System.Int32
부호 있는 32비트 정수
-2,147,483,648 ~ 2,147,483,647
32Bit
uint
System.UInt32
부호 없는 32비트 정수
0 ~ 4,294,967,295
32Bit
long
System.Int64
부호 있는 64비트 정수
-9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
64Bit
ulong
System.UInt64
부호 없는 64비트 정수
0 ~ 18,446,744,073,709,551,615
64Bit
float
System.Single
단정밀도 부동 소수점 숫자
-3.4028235E+38 ~ 3.4028235E+38[3]
4Byte
double
System.Double
배정밀도 부동 소수점 숫자
-1.7976931348623157E+308 ~ 1.7976931348623157E+308[4]
8Byte
decimal
System.Decimal
배정밀도 부동 소수점 숫자
-79,228,162,514,264,337,593,543,950,335 ~ 79,228,162,514,264,337,593,543,950,335
16Byte
6.2.2.1. BigInteger (System.Numerics) [편집]
일반적인 숫자 형식과 다르게 상한선이 없는 큰 정수를 나타내는 BigInteger 형식이 있다.

상한선이 없으므로 MinValue, MaxValue등의 상수를 제공하지 않으며,

값이 지나치게 커지게 되면 OutOfMemoryException(메모리 최대 할당 가능량 초과) 예외가 발생할 수 있다.

초기화는 다음과 같이 할 수 있다.

using System;
using System.Numerics;
// ...

BigInteger bi = new BigInteger(51239932019301293);
BigInteger bi2 = BigInteger.Parse("123456789123456789123456789123456789123456789123456789");

Console.WriteLine(bi);
Console.WriteLine(bi2);

Java와 다르게 공식 BCL에서 BigDecimal과 같이 상한선이 없는 소수점 타입을 제공하지는 않고 있다.

하지만, 다음의 링크를 참조하면 여러 대체제들을 안내하고 있다.
6.2.2.2. 문자(char) [편집]
문자 형식의 경우, char와 string으로 나뉘게 되는데

char은 유니코드 단일 문자를 나타내며, string 문자열을 나타낸다.

char 형식은 System.Char의 별칭이다. 기본 값으로 '0'(U+0000)를 가지고 있다.

char은 여러가지 메서드를 제공하며 다음의 예제는 일부 메서드를 보여주고 있다.

using System;

public class CharStructureSample
{
    public static void Main()
    {
        char chA = 'A';
        char ch1 = '1';
        string str = "test string";

        Console.WriteLine(chA.CompareTo('B'));        // 결과: "-1" ('A' 문자가 'B' 문자보다 작다는 것을 의미함.)
        Console.WriteLine(chA.Equals('A'));           // 결과: "True"
        Console.WriteLine(Char.GetNumericValue(ch1)); // 결과: "1"
        Console.WriteLine(Char.IsControl('\t'));      // 결과: "True"
        Console.WriteLine(Char.IsDigit(ch1));         // 결과: "True"
        Console.WriteLine(Char.IsLetter(','));        // 결과: "False"
        Console.WriteLine(Char.IsLower('u'));         // 결과: "True"
        Console.WriteLine(Char.IsNumber(ch1));        // 결과: "True"
        Console.WriteLine(Char.IsPunctuation('.'));   // 결과: "True"
        Console.WriteLine(Char.IsSeparator(str, 4));  // 결과: "True"
        Console.WriteLine(Char.IsSymbol('+'));        // 결과: "True"
        Console.WriteLine(Char.IsWhiteSpace(str, 4)); // 결과: "True"
        Console.WriteLine(Char.Parse("S"));           // 결과: "S"
        Console.WriteLine(Char.ToLower('M'));         // 결과: "m"
        Console.WriteLine('x'.ToString());            // 결과: "x"
    }
}

또한 유니코드 형식으로 문자 대입도 가능하다.
char ch = '\u0061';    // 'a' 와 동일
char ch2 = '\u0041';   // 'A'와 동일

Console.WriteLine(ch);  // 결과: a
Console.WriteLine(ch2); // 결과: A

6.2.3. 날짜/시간(DateTime) [편집]


C# 날짜와 시간을 나타내는 형식이다. 기본값으로는 "01/01/0001 00:00:00"를 가지고 있다.

DateTime을 초기화하는 방법은 다음과 같다.
DateTime dt = new DateTime(); // "01/01/0001 00:00:00"
DateTime dt2 = DateTime.Now;   // 현재 날짜와 시간을 가져옴
DateTime dt3 = DateTime.Parse("2020-05-29"); // 문자열로부터 날짜를 파싱
DateTime dt4 = DateTime.Parse("2020-05-29 15:00:00"); // 문자열로부터 날짜와 시간을 파싱
DateTime dt5 = dt4.Date; // dt4에서 시간을 제외한 날짜만 가져와 대입


일반적인 == 연산이나 != 연산등이 가능하다.
Console.WriteLine(DateTime.Now.Date == DateTime.Now) // 결과: false
Console.WriteLine(DateTime.Now.Date != DateTime.Now) // 결과: true


날짜나 시간을 추가/제거하기 위해서는 다음과 같다.
DateTime dt = DateTime.Now.AddDays(1); // 현재 날짜/시간에서 하루를 추가
DateTime dt = DateTime.Now.AddDays(1.5); // 현재 날짜/시간에서 하루 + 12시간을 추가 (소숫점의 경우 가장 가까운 밀리초로 반올림)
DateTime dt = DateTime.Now.AddDays(-1); // 현재 날짜/시간에서 하루를 뺀 값

// AddYears, AddHours, AddMinutes, AddSeconds, AddTicks 등의 함수도 지원

6.3. 참조 형식 [편집]

참조 형식은 값 형식과 달리 변수가 직접 데이터를 포함하고 있지 않고 실제 데이터에 대한 참조(관리되는 포인터)가 저장된다. 따라서, 변수에 대한 작업이 다른 변수에서 참조하는 개체에 영향을 미칠 수 있다.

일반적으로, 데이터를 복사해야 할 경우 깊은 복사(Deep Copy)를 통해서 데이터를 완전히 새로 생성한 다음에 작업해야 원본 데이터에 영향이 없다.

6.3.1. object [편집]

object 형식은 System.Object의 별칭이다. 기본 값은 null이다.

Java와 마찬가지로 C#도 모든 형식은 직/간접적으로 System.Object를 상속 받는다. 따라서, object 형식의 변수에는 모든 형식의 값을 할당할 수 있다.

특이점으로는, 값 형식이 object에 할당될 경우 박싱/언박싱이 발생하게 된다.

6.3.2. 문자열(string) [편집]

string은 System.String의 별칭이다. 기본 값으로 null을 가지고 있다. (""을 가지고 있을거라고 생각할 수도있지만, string은 값 형식처럼 보이지만 실질적으로 참조형식이므로, null을 기본 값으로 가지고 있다.)

Java에서는 문자열 비교를 위해서 == 대신에 .equals 함수를 사용하도록 권고하고 있다. (실제로 == 를 사용하게 되면, 참조 형식 비교를 하게 되므로 두가지 변수의 값을 비교하는 것이 아닌 참조 위치를 비교하므로 false를 반환한다.)

하지만, C#의 경우에는 Java와는 달리 ==를 통해서 문자열 비교를 한다. (물론, 기존의 equals 함수를 통해서 비교를 할 수 있으며 이는 대소문자 무시 등을 위해 StringComparison 옵션을 넣을 때 활용된다.)

string의 경우 == 의 동작을 equals 함수와 동일하게 오버로딩하였기 때문에 굳이 "abc".equals("abc")가 아닌 "abc" == "abc"도 예상한 대로 동작하는 것이다.

다만, 주의해야 할 점이 있다면 object 형식으로 캐스팅 했을 경우에는 string 값에서 오버로딩되어 있는 .Equals 함수가 object에서는 오버로딩 되지 않으므로 참조 비교를 해서 ==가 예상한대로 동작하지 않는다.

다만, 이런 경우에 대부분의 IDE에서는 참조 비교에 대해서 경고를 띄워준다.
static void CompareString()
{
    string s1 = "ab";
    string s2 = "abcd".Substring(0, 2);
    object o1 = s2;

    Console.WriteLine(quot;{s1.Equals(s2)}\t{s1.Equals(o1)}");
    Console.WriteLine(quot;{s1 == s2}\t{s1 == o1}");

    // 결과:
    // True    True
    // True    False
    // s1 == o1의 경우 string와 object 간의 비교이므로 == 동작이 string에서처럼 동작하지 않는다.
}

6.4. 리터럴 값 형식 [편집]

6.5. 제네릭 형식 [편집]

6.6. 암시적 형식, 무명 형식, nullable 값 형식 [편집]

7. 함수 (Function) [편집]

7.1. 입출력 함수 [편집]

7.1.1. 콘솔 입출력 [편집]

콘솔[5]에 입출력을 하려면 System.Console 클래스를 사용하면 된다.

출력함수
  • void Console.Write(object value) : 매개변수의 값을 출력
  • void Console.WriteLine(object value) : 매개변수의 값을 출력하고 줄 바꿈(개행)

입력함수
  • int Console.Read() : 한 문자를 입력받고, 그 문자의 유니코드 값을 반환(문자로 변환하려면 (char)Console.Read())처럼 사용)
  • ConsoleKeyInfo Console.ReadKey() : 입력한 키 조합을 반환
  • string Console.ReadLine() : 입력받은 한 문자열을 반환(Enter키로 입력)

출력 예시[6]
using System;

namespace CSharpExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Hello, world!");
        }
    }
}


입력 예시[7]
using System;

namespace CSharpExample
{
    class Program
    {
        static void Main(string[] args)
        {
            string read = Console.ReadLine();
            Console.WriteLine(read.ToUpper());
        }
    }
}
[1] 코드에 기술할 멤버가 많을 때 특징별로 여러 파일에 나누어 작성하면 유용한 기능이다. Windows.Form 프로젝트 품 생성하면 품 디자인 코드는 (품 이름).Designer.cs 파일에 저장된다.[2] 생성자[3] 지수표기법이다.[4] 지수표기법이다.[5] Windows 명령 프롬프트 같은 창[6] 이 코드는 Hello, world! 프로그램의 C# 코드이며, Visual Studio 콘솔 앱 기본 템플릿이기도 하다.[7] 이 코드는 입력받은 문자열을 모두 영문 대문자로 바꾸어 출력하는 프로그램이다.

라이선스를 별도로 명시하지 않은 문서는 CC BY-NC-SA 2.0 KR에 따라 이용할 수 있습니다.
기여하신 문서의 저작권은 각 기여자에게 있으며, 각 기여자는 기여하신 부분의 저작권을 갖습니다.

문서의 기여자는 역사 탭에서 확인할 수 있습니다.
접두어의 N: - 나무위키 사용자, R: - 리그베다 위키의 사용자를 뜻합니다.
자세한 사항은 나무위키에서 동일한 문서의 역사를 참고하시기 바랍니다.