Skip to content

CSharp

C#은 마이크로소프트에서 개발한 객체 지향 프로그래밍 언어로, 닷넷 프레임워크의 한 부분으로 만들었으며 나중에 ECMA (ECMA-334)와 ISO (ISO/IEC/23270)의 표준으로 자리잡았다. C++와 자바와 비슷한 문법을 가지고 있다.

C#은 닷넷 프로그램이 동작하는 닷넷 플랫폼을 가장 직접적으로 반영하고, 또한 닷넷 플랫폼에 강하게 의존하는 프로그래밍 언어이다. C#은 그 문법적인 특성이 자바와 상당히 유사하며 C#을 통하여 다룰 수 있는 닷넷 플랫폼의 기술들조차도 자바를 염두에 둔 것이 많아서 자바와 가장 많이 비교되고 있다. 하지만 C#은 자바와 달리 불안전 코드(unsafe code)와 같은 기술을 통하여 플랫폼 간 상호 운용성에 상당히 많은 노력을 기울이고 있다. C#의 기본 자료형은 닷넷의 객체 모델을 따르고 있고, 런타임 차원에서의 메모리 수거(garbage collection)가 되며 또한 클래스, 인터페이스, 위임, 예외와 같이 객체 지향 언어로서 가져야 할 모든 요소들이 포함되어있다.

특징

C#은 닷넷 프로그램이 동작하는 닷넷 플랫폼을 가장 직접적으로 반영하고, 또한 닷넷 플랫폼에 강하게 의존하는 프로그래밍 언어이다. C#은 그 문법적인 특성이 자바와 상당히 유사하며 C#을 통하여 다룰 수 있는 닷넷 플랫폼의 기술들조차도 자바를 염두에 둔 것이 많아서 자바와 가장 많이 비교되고 있다. 하지만 C#은 자바와 달리 불안전 코드(unsafe code)와 같은 기술을 통하여 플랫폼 간 상호 운용성에 상당히 많은 노력을 기울이고 있다. C#의 기본 자료형은 닷넷의 객체 모델을 따르고 있고, 런타임 차원에서의 쓰레기 수집(garbage collection)이 되며 또한 클래스, 인터페이스, 위임, 예외와 같이 객체 지향 언어로서 가져야 할 모든 요소들이 포함되어 있다.

Categories

System

Syntax

.NET Categories

Examples

Libraries

GUI Libraries

C Sharp syntax

전처리기 지시문

Region

구역을 나누는 용도. objc의 #pragma mark와 비슷한 용도.

#region CmdLineStuff
......
#endregion CmdLineStuff

Hello, world!

using System;

namespace HelloWorld
{
    class Program
    {
        private static void Main()
        {
            Console.WriteLine("Hello, world!");
        }
    }
}

String interpolation

자세한 내용은 String interpolation 항목 참조.

string name = "Mark";
var date = DateTime.Now;

// Composite formatting:
Console.WriteLine("Hello, {0}! Today is {1}, it's {2:HH:mm} now.", name, date.DayOfWeek, date);
// String interpolation:
Console.WriteLine($"Hello, {name}! Today is {date.DayOfWeek}, it's {date:HH:mm} now.");
// Both calls produce the same output that is similar to:
// Hello, Mark! Today is Wednesday, it's 19:40 now.

소수점 자리수 제한

Console.Write($"{aDoubleValue:f2}");

try ... catch ... when

e.Datanull일 때 함수는 호출자에게 InvalidCastException을 다시 throw합니다.

catch (InvalidCastException e) when (e.Data != null)
{
    // Take some action.
}

interface vs abstract

interface

public interface CallbackInterface
{
    void OnRhythmComplete(int successCount, int failureCount, int readyCount);
    void OnRhythmChangeCursor(int measureNumber, int beatNumber, float progressPercentage);
}

여러 개를 상속(구현) 받을 수 있다.

abstract

public abstract class BaseElementInterface
{
    public abstract void OpenAttributeImpl(Xml.XmlNode node);
    public abstract void OpenElementImpl(Xml.XmlNode node);
    public abstract void ClearImpl();

    public abstract string ToPrintableText();
    public abstract string ToParamsText();
}

1개만 상속받을 수 있다.

Array Element Access Overload

  • [How do I overload the operator in C# - Stack Overflow]
public int this[int key]
{
    get => GetValue(key);
    set => SetValue(key, value);
}

Default Optional Parameter

default라는 키워드를 사용하면 해당 변수의 기본 값을 사용한다. 다만 변수명도 default로 사용하고 싶다면 키워드 충돌을 피하기 위해 @default와 같은 형식도 사용할 수 있다.

private Variant _Get(string section, string key, Variant @default = default /* `new Variant()` 와 동일하다. */ )
{
    return _config.GetValue(section, key, @default);
}

const vs readonly

internal

  • internal - C# 참조 | Microsoft Learn
  • internal 키워드는 형식 및 형식 멤버에 대한 액세스 한정자입니다.
  • internal 키워드는 protected internal 액세스 한정자의 일부이기도 합니다.

내부 형식 또는 멤버는 다음 예제와 같이 동일한 어셈블리의 파일 내에서만 액세스할 수 있습니다.

문자열을 Enum 으로 파싱

public enum Colors { None=0, Red = 1, Green = 2, Blue = 4 };
// ...
Colors colorValue = (Colors) Enum.Parse(typeof(Colors), colorString, true);

@ 심볼의 사용법

@ 심벌을 문자열 앞에 사용하면, 해당 문자열 안의 Escape 문자를 무시하고 문자 그대로 인식하도록 한다. 예를 들어, 파일 패스를 지정할 때, Backslash를 한번 지정하면 이는 Escape문자로 인식되기 때문에 2개의 Backslash를를 사용하게 되는데, @ 심벌을 문자열 시작 부호전에 사용하면, Backslash를를 그대로 Backslash를로 인식하게 한다.

string filename = "C:\\Temp\\1.txt";

// @심벌을 사용하여 보다 자연스럽게 패스 지정
string filename = @"C:\Temp\1.txt";

// 여러 줄도 가능하다:
string code = @"
public string ReadFile(string filename)
{
    if (!string.IsNullOrEmpty(filename))
    {
        return File.ReadAllText(filename);
    }
    return string.Empty;
}
";

Console.WriteLine(code);

마지막으로 @ 심벌은 C# 키워드 앞에 붙여 일반 변수명으로 사용할 때 유용하게 활용된다. 아래 예제에서 보이듯이, object는 C# 키워드 이므로 string object = "객체" 와 같이 쓸 수 없다. 즉, 이렇게 하면 컴파일 에러가 발생할 것이다. 하지만 object C# 키워드 앞에 @ 사인을 붙이면 변수명으로 사용할 수 있다. 하지만, 이렇게 사용할 수 있다고 해서 굳이 변수명을 object로 사용하도록 권장하는 사람은 아무도 없다. 오히려 반대로 대부분 C# 키워드는 변수명으로 사용하지 말도록 권장한다.

public void Run()
{
    // object라는 변수명 지정
    string @object = "객체";

    @object = @object + "1";
    Console.WriteLine(@object);
}

그렇다면 이 기능은 왜 필요할까?

아래 예제를 살펴보자. 이는 ASP.NET MVC의 Html Helper의 예를 보여주는데, ActionLink 메서드의 4번째 파라미터를 보면, 익명타입 (Anonymous Type)의 객체를 생성한 후 Html Attribute와 관련된 속성들을 지정하고 있고 있다. 그런데 여기서 class는 C#의 키워드 이므로 변수명으로 사용할 수 없는데, 실제 Html 속성 class는 이미 표준에 의해 지정된 것이므로 @class와 같이 사용해야만 한다.

// ASP.NET MVC Html Helper
@Html.ActionLink("Goto Menu", "Menu", null,
     new { @class="linkStyle", target="_blank"});

<A href="/Home/Menu" class="linkStyle" target="_blank">
   Goto Menu
</A>

Property getter vs Method getter

public string MyProp { get; } - 이것은 C # 6.0에서 도입되었습니다. 이러한 속성을 읽기 전용 자동 속성이라고합니다. 이러한 멤버에 대한 할당은 선언의 일부 또는 동일한 클래스의 생성자에서만 발생할 수 있습니다. MSDN 기사 또는 Jon Skeet 블로그에서 자세한 설명을 읽을 수 있습니다. 이 기사에서 설명했듯이 이러한 속성은 다음 네 가지 문제를 자동으로 해결합니다.

  • A read-only-defined backing field
  • Initialization of the backing field from within the constructor
  • Explicit implementation of the property (rather than using an auto-property)
  • An explicit getter implementation that returns the backing field

public string MyProp { get; private set; } - 이 속성은이 클래스 외부에서는 읽기 전용이지만이 클래스 내부에서 값을 변경할 수 있음을 의미합니다.

그런데 C # 6.0에 다시 도입 된 새로운 자동 초기화 구문을 사용하여 읽기 전용 자동 속성 값을 설정할 수 있습니다.

public string MyProp { get; } = "You cannot change me";

이전 버전의 C#에 대한 다음 코드와 같습니다:

private readonly string myProp = "You cannot change me"
public string MyProp { get { return myProp ; } }

또는 이것은 C# 6.0에서:

public string MyProp { get; }
protected MyClass(string myProp, ...)
{
    this.MyProp = myProp;
    ...
}

이전 버전에서 다음과 같습니다.

private readonly string myProp;
public string MyProp { get { return myProp; } }
protected MyClass(string myProp, ...)
{
    this.myProp = myProp;
    ...
}

함수형을 위한 대체 메서드

Linq를 사용해야 한다. Python 의 Map is Select:

Enumerable.Range(1, 10).Select(x => x + 2);

Python 의 Reduce is Aggregate:

Enumerable.Range(1, 10).Aggregate(0, (acc, x) => acc + x);

Python 의 Filter is Where:

Enumerable.Range(1, 10).Where(x => x % 2 == 0);

파일 목록 조회

import System.IO;

// ...

foreach (string file in Directory.EnumerateFiles(path, "*.*", SearchOption.AllDirectories))
{
    Console.WriteLine(file);
}

디렉토리 목록 재귀적 조회:

static void DirSearch(string sDir)
{
    try
    {
        foreach (string d in Directory.GetDirectories(sDir))
        {
            foreach (string f in Directory.GetFiles(d))
            {
                Console.WriteLine(f);
            }
            DirSearch(d);
        }
    }
    catch (System.Exception excpt)
    {
        Console.WriteLine(excpt.Message);
    }
}

Folder Browser Dialog

코드로 생성:

FolderBrowserDialog fbd = new FolderBrowserDialog();
if (fbd.ShowDialog() == DialogResult.OK)
{
    // ...
}

컴포넌트로 출력:

public void ChooseFolder()
{
    if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
    {
        textBox1.Text = folderBrowserDialog1.SelectedPath;
    }
}

Create file or directory

public class CreateFileOrFolder
{
    static void Main()
    {
        // Specify a name for your top-level folder.
        string folderName = @"c:\Top-Level Folder";

        // To create a string that specifies the path to a subfolder under your
        // top-level folder, add a name for the subfolder to folderName.
        string pathString = System.IO.Path.Combine(folderName, "SubFolder");

        // You can write out the path name directly instead of using the Combine
        // method. Combine just makes the process easier.
        string pathString2 = @"c:\Top-Level Folder\SubFolder2";

        // You can extend the depth of your path if you want to.
        //pathString = System.IO.Path.Combine(pathString, "SubSubFolder");

        // Create the subfolder. You can verify in File Explorer that you have this
        // structure in the C: drive.
        //    Local Disk (C:)
        //        Top-Level Folder
        //            SubFolder
        System.IO.Directory.CreateDirectory(pathString);

        // Create a file name for the file you want to create.
        string fileName = System.IO.Path.GetRandomFileName();

        // This example uses a random string for the name, but you also can specify
        // a particular name.
        //string fileName = "MyNewFile.txt";

        // Use Combine again to add the file name to the path.
        pathString = System.IO.Path.Combine(pathString, fileName);

        // Verify the path that you have constructed.
        Console.WriteLine("Path to my file: {0}\n", pathString);

        // Check that the file doesn't already exist. If it doesn't exist, create
        // the file and write integers 0 - 99 to it.
        // DANGER: System.IO.File.Create will overwrite the file if it already exists.
        // This could happen even with random file names, although it is unlikely.
        if (!System.IO.File.Exists(pathString))
        {
            using (System.IO.FileStream fs = System.IO.File.Create(pathString))
            {
                for (byte i = 0; i < 100; i++)
                {
                    fs.WriteByte(i);
                }
            }
        }
        else
        {
            Console.WriteLine("File \"{0}\" already exists.", fileName);
            return;
        }

        // Read and display the data from your file.
        try
        {
            byte[] readBuffer = System.IO.File.ReadAllBytes(pathString);
            foreach (byte b in readBuffer)
            {
                Console.Write(b + " ");
            }
            Console.WriteLine();
        }
        catch (System.IO.IOException e)
        {
            Console.WriteLine(e.Message);
        }

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }
    // Sample output:

    // Path to my file: c:\Top-Level Folder\SubFolder\ttxvauxe.vv0

    //0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
    //30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
    // 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 8
    //3 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
}

Lock

private object lockObject = new object();
// ...
lock (lockObject)
{
    // ...
}

MemoryStream and StringWriter

// ...
    public string GetIdentificationText()
    {
        using (MemoryStream stream = new MemoryStream())
        {
            using (StreamWriter writer = new StreamWriter(stream))
            {
                writer.WriteLine("[Identification]");
                foreach (var right in _scorePartwise.Identification.Rights)
                {
                    writer.WriteLine($"- Right: {right.Value}");
                }

                var encoding = _scorePartwise.Identification.Encoding;
                foreach (var encoder in encoding.Encoder)
                {
                    writer.WriteLine($"- Encoder: {encoder.Value}");
                }
                foreach (var encodingDate in encoding.EncodingDate)
                {
                    writer.WriteLine($"- Encoding Date: {encodingDate}");
                }
                foreach (var encodingDescription in encoding.EncodingDescription)
                {
                    writer.WriteLine($"- Encoding Description: {encodingDescription}");
                }
                foreach (var software in encoding.Software)
                {
                    writer.WriteLine($"- Software: {software}");
                }
                foreach (var supports in encoding.Supports)
                {
                    var e = supports.Element;
                    var t = supports.Type;
                    var v = supports.Value;
                    var a = supports.Attribute;
                    writer.WriteLine($"- Software: element={e}, type={t}, value={v}, attribute={a}");
                }
            }
            return System.Text.Encoding.UTF8.GetString(stream.ToArray());
        }
    }
// ...

Syntax

Array & Rank

배열은 차원(Rank), 인덱스(Index), 값(Element)를 가지고 있다.

다차원 배열과 그 확인 방법에 대한 예제는 아래와 같다.

// Two-dimensional array.
int[,] array2D = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
// The same array with dimensions specified.
int[,] array2Da = new int[4, 2] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
// A similar array with string elements.
string[,] array2Db = new string[3, 2] { { "one", "two" }, { "three", "four" },
                                        { "five", "six" } };

// Three-dimensional array.
int[, ,] array3D = new int[,,] { { { 1, 2, 3 }, { 4, 5, 6 } }, 
                                 { { 7, 8, 9 }, { 10, 11, 12 } } };
// The same array with dimensions specified.
int[, ,] array3Da = new int[2, 2, 3] { { { 1, 2, 3 }, { 4, 5, 6 } }, 
                                       { { 7, 8, 9 }, { 10, 11, 12 } } };

for (int i = 0; i < array3D.Rank; i++) {
    total *= array3D.GetLength(i);
}

참고로 GetLength은 낮은 Index값을 파라미터로 전달할 경우 높은 차 수의 길이가 반환된다.

int[,] array2Da = new int[4, 2] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };
array2Da.GetLength(0); // Return 4
array2Da.GetLength(1); // Return 2

Random

Random r = new Random();
r.Next(); // 랜덤값 생성
r.Next(Int32 min, Int32 max); // 최소 min 값 부터 최대 max - 1 까지

See also

Favorite site

Tutorials

Demo