반응형

제너릭 프로그래밍


장점은 하단에..



제네릭 메소드와 클래스 예제

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
using System;
 
namespace GenericEx
{
    class ClassA
    {
        public static void CopyArray(int[] src, int[] target)
        {
            for (int i = 0; i < src.Length; ++i)
            {
                target[i] = src[i];
            }
        }
        
        public static void CopyArray(string[] src, string[] target)
        {
            for (int i = 0; i < src.Length; ++i)
            {
                target[i] = src[i];
            }
        }
        
        // 제네릭 메소드
        public static void GenericCopyArray<T>(T[] src, T[] target)
        {
            for (int i = 0; i < src.Length; ++i)
            {
                target[i] = src[i];
            }
        }
    }
    
    // 제네릭 클래스
    class GenericClassA<T>
    {
        public static void GenericCopyArray(T[] src, T[] target)
        {
            for (int i = 0; i < src.Length; ++i)
            {
                target[i] = src[i];
            }
        }
    }
 
    class MainClass
    {
        public static void Main (string[] args)
        {
                int[] iSrc = { 12345 };
                int[] iTarget = new int[5];
                
                string[] strSrc = { "ab""cd""ef""gh""ij" };
                string[] strTarget = new string[5];
                
                ClassA.CopyArray(iSrc, iTarget);
                foreach (int i in iTarget)
                    Console.WriteLine(i);
                
                ClassA.CopyArray(strSrc, strTarget);
                foreach (string s in strTarget)
                    Console.WriteLine(s);
                
                float[] fSrc = { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f };
                float[] fTarget = new float[5];
                
                // 제네릭 메소드의 활용
                ClassA.GenericCopyArray<float>(fSrc, fTarget);
                foreach (float f in fTarget)
                    Console.WriteLine(f);
                
                // 제네릭 클래스의 활용
                GenericClassA<float>.GenericCopyArray(fSrc, fTarget);
                foreach (float f in fTarget)
                    Console.WriteLine(f);
                
                Console.ReadKey();
        }
    }
}
cs

LinkedList<T> 예제

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
83
84
85
86
87
88
89
90
91
92
93
using System;
using System.Text;
using System.Collections.Generic;
 
namespace LinkedListTEx
{
    class MainClass
    {
        private static void Display(LinkedList<string> words, string test)
        {
            Console.WriteLine(test);
            foreach (string word in words)
            {
                Console.Write(word + " ");
            }
            Console.WriteLine();
            Console.WriteLine();
        }
        
        private static void IndicateNode(LinkedListNode<string> node, string test)
        {
            Console.WriteLine(test);
            if (node.List == null)
            {
                Console.WriteLine("Node '{0}' is not in the list.\n",
                                  node.Value);
                return;
            }
            
            StringBuilder result = new StringBuilder("(" + node.Value + ")");
            LinkedListNode<string> nodeP = node.Previous;
            
            while (nodeP != null)
            {
                result.Insert(0, nodeP.Value + " ");
                nodeP = nodeP.Previous;
            }
            
            node = node.Next;
            while (node != null)
            {
                result.Append(" " + node.Value);
                node = node.Next;
            }
            
            Console.WriteLine(result);
            Console.WriteLine();
        }
 
        public static void Main (string[] args)
        {
            string[] words = { "유니티""C#으로""스크립팅""한다"};
 
            // 연결 리스트의 생성
            LinkedList<string> sentence = new LinkedList<string>(words);
 
            Display(sentence, "연결 리스트의 값은:");
            Console.WriteLine("sentence.Contains(\"유니티\") = {0}", sentence.Contains("유니티"));
 
            sentence.AddFirst("오늘은");
            Display(sentence, "Test 1: 리스트 앞에 '오늘은' 문자열 추가");
 
            LinkedListNode<string> mark1 = sentence.First;
            sentence.RemoveFirst();
            sentence.AddLast(mark1);
            Display(sentence, "Test 2: 마지막 단어를 끝으로 이동");
 
            sentence.RemoveLast();
            sentence.AddLast("오늘만");
            Display(sentence, "Test 3: 마지막 단어를 '오늘만' 으로 변경");
 
            mark1 = sentence.Last;
            sentence.RemoveLast();
            sentence.AddFirst(mark1);
            Display(sentence, "Test 4: 마지막 노드를 첫 노드로 이동");
            
            sentence.RemoveFirst();
            LinkedListNode<string> current = sentence.FindLast("C#으로");
            IndicateNode(current, "Test 5: '스크립팅'이라는 단어를 찾아 가리킨다 ");
            
            sentence.AddAfter(current, "자바스크립트로");
            sentence.AddAfter(current, "그리고");
            IndicateNode(current, "Test 6: '스크립팅' 단어 뒤에 단어 추가");
 
            current = sentence.Find("유니티");
            IndicateNode(current, "Test 7: '유니티' 노드를 가리킨다");
 
            sentence.AddBefore(current, "오늘과");
            sentence.AddBefore(current, "내일은");
            IndicateNode(current, "Test 8: '오늘과', '내일은' 단어를 '유니티' 노드 앞에 추가");
        }
    }
}
cs

List<T> 예제

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
using System;
using System.Collections.Generic;
 
namespace ListTEx
{    
    public class Enemy {
        public string Name { get; set; }        
        public int Level { get; set; }
        public int HP { get; set; }
        public int Exp { get; set; }
                
        public void printEnemyInfo() {
            Console.WriteLine ("Name : {0} Level : {1} HP : {2} Exp : {3}", Name, Level, HP, Exp);
        }
    }
 
    class MainClass
    {
        public static List<Enemy> enemyList;
 
        public static void Main (string[] args)
        {
            enemyList = new List<Enemy>();
            // 추가
            enemyList.Add (new Enemy () { Name = "Slime", Level = 1, HP = 200, Exp = 30 });
            enemyList.Add (new Enemy () { Name = "Zombie", Level = 2, HP = 100, Exp = 50 });
            enemyList.Add (new Enemy () { Name = "Skeleton", Level = 3, HP = 120, Exp = 80 });
            enemyList.Add (new Enemy () { Name = "Bugbear", Level = 4, HP = 300, Exp = 150 });
 
            // 삽입
            enemyList.Insert (2new Enemy () { Name = "Bugbear", Level = 5, HP = 350, Exp = 180 });
 
            Console.WriteLine ("**********************");
            foreach (Enemy enemy in enemyList) {
                enemy.printEnemyInfo();
            }
 
            // 삭제
            enemyList.RemoveAt (2);
 
            // 검색 후 삭제
            Enemy bugBear = enemyList.Find (x => x.Name.Equals ("Bugbear"));
            enemyList.Remove (bugBear);
 
            Console.WriteLine ("**********************");
            foreach (Enemy enemy in enemyList) {
                enemy.printEnemyInfo();
            }
        }
    }
}
cs

Dictionary<TKey, TValue> 예제

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
using System;
using System.Collections.Generic;
 
namespace DictionaryTEx
{
    public class FileFormatTable {
        private Dictionary<stringstring> openWith;
        
        public FileFormatTable() {
            openWith = new Dictionary<stringstring>();
            
            // 추가
            openWith.Add("txt""notepad.exe");
            openWith.Add("bmp""paint.exe");
            openWith.Add("dib""paint.exe");
            openWith.Add("rtf""wordpad.exe");
            openWith.Add("odf""wordpad.exe");
            
            // 이미 할당 된 키일 경우 
            try
            {
                openWith.Add("txt""winword.exe");
            }
            catch
            {
                Console.WriteLine("An element with Key = \"txt\" already exists.");
            }
            
            // 제거
            openWith.Remove("odf");
        }
        
        public void printTable() {
            // 탐색
            foreach (KeyValuePair<stringstring> d in openWith)
                Console.WriteLine("Key = {0}, Value = {1}", d.Key, d.Value);
        }
    }
 
    class MainClass
    {
        public static void Main (string[] args)
        {
            FileFormatTable fileFormatTable = new FileFormatTable ();
            fileFormatTable.printTable ();
        }
    }
}
cs

Queue<T> 예제

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
using System;
using System.Collections.Generic;
 
namespace QueueTEx
{
    public class Enemy {
        public string Name { get; set; }        
        public int Level { get; set; }
        public int HP { get; set; }
        public int Exp { get; set; }
        
        public void printEnemyInfo() {
            Console.WriteLine ("Name : {0} Level : {1} HP : {2} Exp : {3}", Name, Level, HP, Exp);
        }
    }
 
    class MainClass
    {
        public static Queue<Enemy> enemyQueue;
 
        public static void Main (string[] args)
        {
            enemyQueue = new Queue<Enemy>();
            // 추가
            enemyQueue.Enqueue (new Enemy () { Name = "Slime", Level = 1, HP = 200, Exp = 30 });
            enemyQueue.Enqueue (new Enemy () { Name = "Zombie", Level = 2, HP = 100, Exp = 50 });
            enemyQueue.Enqueue (new Enemy () { Name = "Skeleton", Level = 3, HP = 120, Exp = 80 });
            enemyQueue.Enqueue (new Enemy () { Name = "Bugbear", Level = 4, HP = 300, Exp = 150 });
            enemyQueue.Enqueue (new Enemy () { Name = "Bugbear", Level = 5, HP = 350, Exp = 180 });
            
            Console.WriteLine ("**********************");
            foreach (Enemy enemy in enemyQueue) {
                enemy.printEnemyInfo();
            }
            
            // 삭제
            Console.WriteLine ("********* Out *********");
            Enemy outEnemy = enemyQueue.Dequeue ();
            outEnemy.printEnemyInfo ();
 
            Console.WriteLine ("**********************");
            foreach (Enemy enemy in enemyQueue) {
                enemy.printEnemyInfo();
            }
 
            // 검색 후 삭제
            Console.WriteLine ("******** First *********");
            Enemy firstEnemy = enemyQueue.Peek();
            firstEnemy.printEnemyInfo ();
        }
    }
}
cs



Stack<T> 예제

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
using System;
using System.Collections.Generic;
 
namespace StackTEx
{
    public class Enemy {
        public string Name { get; set; }        
        public int Level { get; set; }
        public int HP { get; set; }
        public int Exp { get; set; }
        
        public void printEnemyInfo() {
            Console.WriteLine ("Name : {0} Level : {1} HP : {2} Exp : {3}", Name, Level, HP, Exp);
        }
    }
 
    class MainClass
    {
        public static Stack<Enemy> enemyStack;
 
        public static void Main (string[] args)
        {
            enemyStack = new Stack<Enemy>();
            // 추가
            enemyStack.Push (new Enemy () { Name = "Slime", Level = 1, HP = 200, Exp = 30 });
            enemyStack.Push (new Enemy () { Name = "Zombie", Level = 2, HP = 100, Exp = 50 });
            enemyStack.Push (new Enemy () { Name = "Skeleton", Level = 3, HP = 120, Exp = 80 });
            enemyStack.Push (new Enemy () { Name = "Bugbear", Level = 4, HP = 300, Exp = 150 });
            enemyStack.Push (new Enemy () { Name = "Bugbear", Level = 5, HP = 350, Exp = 180 });
            
            Console.WriteLine ("**********************");
            foreach (Enemy enemy in enemyStack) {
                enemy.printEnemyInfo();
            }
            
            // 삭제
            Console.WriteLine ("********* Out *********");
            Enemy outEnemy = enemyStack.Pop ();
            outEnemy.printEnemyInfo ();
            
            Console.WriteLine ("**********************");
            foreach (Enemy enemy in enemyStack) {
                enemy.printEnemyInfo();
            }
            
            // 검색 후 삭제
            Console.WriteLine ("******** Last *********");
            Enemy lastEnemy = enemyStack.Peek();
            lastEnemy.printEnemyInfo ();
        }
    }
}
cs



출처: http://codepump.tistory.com/56 [CodePump.NET]








제네릭의 장점

 

제네릭을 사용하면 이전 버전의 공용 언어 런타임과 C# 언어에 적용되었던 제한 사항을 해결할 수 있습니다. 이전 버전에서는 유니버설 기본 형식인 Object와 형식 사이의 캐스팅을 통해 일반화를 수행했습니다. 제네릭 클래스를 만들면 컴파일 타임에 형식이 안전한 컬렉션을 만들 수 있습니다.

제네릭이 아닌 컬렉션 클래스를 사용하는 경우의 제한 사항을 보여 주는 예로는 .NET Framework 클래스 라이브러리에서 ArrayList 컬렉션 클래스를 사용하는 간단한 프로그램을 작성하는 경우를 들 수 있습니다. ArrayList는 참조나 값 형식을 저장하기 위해 수정하지 않고도 사용할 수 있는 매우 편리한 컬렉션 클래스입니다.

 

// The .NET Framework 1.1 way to create a list:
System.Collections.ArrayList list1 = new System.Collections.ArrayList();
list1.Add(3);
list1.Add(105);

System.Collections.ArrayList list2 = new System.Collections.ArrayList();
list2.Add("It is raining in Redmond.");
list2.Add("It is snowing in the mountains.");

 

그러나 이러한 편리함에는 상응하는 대가가 따릅니다. ArrayList에 추가되는 모든 참조나 값 형식은 Object에 암시적으로 업캐스팅됩니다. 항목이 값 형식이면 이를 목록에 추가할 때 boxing해야 하고 이를 검색할 때 unboxing해야 합니다. 캐스팅이나 boxing 및 unboxing 작업은 모두 성능을 저하시킵니다. 큰 컬렉션을 반복해야 하는 시나리오에서는 boxing과 unboxing의 영향을 결코 무시할 수 없습니다.

다른 제한 사항으로는 컴파일 타임에 형식을 검사할 수 없다는 점을 들 수 있습니다. ArrayList는 Object에 모든 항목을 캐스팅하므로 컴파일 타임에 클라이언트 코드가 다음과 같은 작업을 수행하지 못하도록 막을 수 없습니다.

 

System.Collections.ArrayList list = new System.Collections.ArrayList();
// Add an integer to the list.
list.Add(3);
// Add a string to the list. This will compile, but may cause an error later.
list.Add("It is raining in Redmond.");

int t = 0;
// This causes an InvalidCastException to be returned.
foreach (int x in list)
{
    t += x;
}

유형이 다른 컬렉션을 만드는 경우 규칙을 정확히 따르는 의도적인 선택일지라도 문자열과 ints를 단일 ArrayList에 결합하면 프로그래밍 오류가 발생할 확률이 더 커지고 이러한 오류는 런타임 이전에 발견할 수 없습니다.

버전 1.0 및 1.1의 C# 언어에서는 고유한 형식별 컬렉션을 작성하는 방법으로만 .NET Framework 기본 클래스 라이브러리 컬렉션 클래스에서 코드를 일반화하는 위험을 방지할 수 있었습니다. 물론 이러한 클래스는 여러 데이터 형식에 다시 사용할 수 없으므로 일반화의 이점이 사라지고 저장하려는 각 형식에 대해 클래스를 다시 작성해야만 합니다.

ArrayList 및 기타 유사한 클래스에 실제로 필요한 것은 클라이언트 코드에서 사용하려는 특정 데이터 형식을 인스턴스별로 지정할 수 있는 방법입니다. 이렇게 하면 T:System.Object로 업캐스팅할 필요가 없어지고 컴파일러에서 형식을 검사할 수도 있게 됩니다. 즉, ArrayList에는 형식 매개 변수가 필요합니다. 제네릭은 바로 이러한 요구 사항을 충족시킵니다. N:System.Collections.Generic 네임스페이스의 제네릭 List<(Of <(T>)>) 컬렉션에서는 컬렉션에 항목을 추가하는 동일한 작업을 다음과 같이 수행할 수 있습니다.

 

// The .NET Framework 2.0 way to create a list
List<int> list1 = new List<int>();

// No boxing, no casting:
list1.Add(3);

// Compile-time error:
// list1.Add("It is raining in Redmond.");




반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

String 문자열(C# 프로그래밍 가이드)  (0) 2017.09.18
제네릭 Collection(컬렉션) 개체  (0) 2017.09.08
using 과 Dispose()  (0) 2017.07.19
확장메서드 public static class  (0) 2017.07.18
프로퍼티 value  (0) 2015.11.17
반응형

C# Using 문 

블락  벗어나면 자동 Dispose()되고 예외 상황에서도 Dispose 처리가 된다

근대 번역이 뭔가 되게 이상한데..;




출처 :  https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/using-statement

예제

다음 예제에서는 using 문을 사용하는 방법을 보여 줍니다.

C#
using (Font font1 = new Font("Arial", 10.0f)) 
{
    byte charset = font1.GdiCharSet;
}

주의

File  Font는 관리되지 않는 리소스에 액세스하는 관리되는 형식의 예입니다(이 경우 파일 핸들 및 장치 컨텍스트). 다른 많은 종류의 관리되지 않는 리소스 및 이를 캡슐화하는 클래스 라이브러리 형식이 있습니다. 그러한 모든 형식은 IDisposable 인터페이스를 구현해야 합니다.



일반적으로 IDisposable 개체를 사용할 경우 using 문에서 선언 및 인스턴스화해야 합니다. using 문은 올바른 방법으로 개체에서 Dispose 메서드를 호출하며, (앞서 설명한 대로 사용하는 경우) Dispose가 호출되자마자 개체 자체가 범위를 벗어나도록 만듭니다. using 블록 내에서 개체는 읽기 전용이며 수정하거나 다시 할당할 수 없습니다.


using 문을 사용하면 개체에서 메서드를 호출하는 동안 예외가 발생하더라도 Dispose가 호출됩니다. try 블록 내부에 개체를 배치하고 finally 블록에서 Dispose를 호출해도 동일한 결과를 얻을 수 있습니다. 실제로 컴파일러는 이 방법으로 using 문을 변환합니다. 이전의 코드 예제는 컴파일 시 다음 코드로 확장됩니다(개체의 제한된 범위를 만드는 여분의 중괄호 참고).

C#
{
  Font font1 = new Font("Arial", 10.0f);
  try
  {
    byte charset = font1.GdiCharSet;
  }
  finally
  {
    if (font1 != null)
      ((IDisposable)font1).Dispose();
  }
}






여러분, using 키워드에 대해 아시나요?
보통 Namespace 를 Import 할때 쓰이죠.

using System.IO; public static class F { public bool Exist(string path) { return File.Exist(path); } }

이런식으로요! 네이버 코드 컴포먼트가 언어를 잘 인식 못하는거 같은데 기분 탓이려나..

하지만, 제가 알아낸 using 기능은 네임스페이스의 Import 외에도 2가지 용도가 더 있습니다!

먼저, 첫번째!
IDispose 인터페이스에게 상속 받은 클래스들을 사용할 때 쓰입니다.
설명을 뭐라 해야할지 모르겠군요.
IDispose 인터페이스를 상속한 클래스들은 Dispose() 메서드가 있는데요. using 을 사용하면 using 문 밖으로 나가는 순간 그 클래스를 자동으로 Dispose() 처리 해 줍니다. 예시를 볼까요?

using (BinaryReader sr = new BinaryReader(new FileStream(File.Open(@".\test.txt"))) { MessageBox.Show(sr.ReadString()); }

진짜로 언어 인식을 잘 못하는거 같은데.
이런식으로요! 자동으로 Dispose 를 해주니 메모리 관리하기에 더 효율적이죠..! 제가 찾은 마지막 하나!
클래스(class) 나 구조체(struct), 인터페이스(interface), 열거자(enum)를 줄여서 사용하고자 할때 도 사용 됩니다.
이것 역시 제가 설명을 잘 못하겠네요. 일단 예시를 보시죠!
이건 네임스페이스 Import 하듯이 사용하시면 됩니다.

using Environ = System.Environment;

언어를 인식 못하는게 맞는거 같다.
이런식으로요! 이렇게 해 두시면

System.Environment.StartupPath

이런식으로 호출 해야 하는것을

Environ.StartupPath

이렇게 호출할 수 있습니다..!
다른 예시도 하나 보여 드리죠.

using Dis = System.IDispose; public class Test : Dis // 원래는 System.IDispose 이렇게 했어야 했으나 using 을 사용해서 줄였으므로 Dis 로 사용 가능. { public void Dispose() { base.Dispose(); } }

이런식으로요..!

그럼 전 이만 물러 나겠습니다.
좋은 정보 되셨으면 좋겠네요!


반응형
반응형

확장 메서드를 사용하기 위해서 클래스를 만드는데 이때 클래스명이 문법적의 컴파일 오류에 있어서 중요한것은 아니고 


public static void method(타입 src, ... ){
}


의 함수에서 타입 자리에 어떤 타입이 오는지에 따라 호출 할 수있는 원본 클래스가 결정된다는 부분이 구분역할로써 중요하다





\



C# 확장 메서드 (Extension Method) 

C# 3.0부터 지원하는 확장메서드(Extension Method)는 특수한 종류의 static 메서드로서 마치 다른 클래스의 인스턴스 메서드인 것처럼 사용된다. 일반적으로 instance 메서드를 추가하기 위해서는 해당 클래스안에 메서드를 추가한다. 만약 여러 개의 클래스들에 instance 메서드를 추가하고 싶다면, 각 클래스마다 메서드를 추가해 주어야 한다 (물론 Base Class가 있는 경우, 클래스 상속을 이용할 수도 있다). 

확장 메서드는 메서드가 사용될 클래스명(혹은 Type)을 첫번째 파라미터로 지정하여 마치 해당 클래스(혹은 Type)가 확장메서드를 인스턴스 메서드로 갖는 것과 같은 효과를 낸다. 약간 특이한 문법이지만, 확장 메서드의 첫번째 파라미터는 앞에 항상 this를 써준다.

아래 예제는 String 클래스를 첫번째 파라미터로 갖는 확장메서드 즉 String 클래스에서만 동작하는 확장 메서드를 정의한 예이다. 

using System;
using System.Text;

namespace MySystem
{
   // static class를 정의
   public static class ExClass
   {
      // static 확장메서드를 정의. 첫번째 파라미터는
      // 어떤 클래스가 사용할 지만 지정. 
      public static string ToChangeCase(this String str)
      {
         StringBuilder sb = new StringBuilder();
         foreach (var ch in str)
         {
            if (ch >= 'A' && ch <= 'Z')
               sb.Append((char)('a' + ch - 'A'));
            else if (ch >= 'a' && ch <= 'x')
               sb.Append((char)('A' + ch - 'a'));
            else
               sb.Append(ch);
         }
         return sb.ToString();
      }

      // 이 확장메서드는 파라미터 ch가 필요함
      public static bool Found(this String str, char ch)
      {
         int position = str.IndexOf(ch);
         return position >= 0;
      }
   }

   class Program
   {
      static void Main(string[] args)
      {
         string s = "This is a Test";
         
         // s객체 즉 String객체가
         // 확장메서드의 첫 파리미터임
         // 실제 ToChangeCase() 메서드는
         // 파라미터를 갖지 않는다.
         string s2 = s.ToChangeCase();

         // String 객체가 사용하는 확장메서드이며
         // z 값을 파라미터로 사용
         bool found = s.Found('z');
      }
   }
}


http://www.csharpstudy.com/CSharp/CSharp-extension-method.aspx




다른 예제



public bool Contains(String value);


해당 문자열에 value 값이 포함되있는지 비교한다. 


해당 키워드는 대소문자를 구문한다.


 


만약 대소문자를 비교하려면 StringExtensions 기능을 이용하여 만들어줘야한다.

(C#은 Extensions를 이용하여 객체 내부의 명시된 클래스를 사용하여 기능을 확장을 정의할 수 있다. )





이렇게 대소문자 구분 무시

 



반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

제너릭의 장점과 boxing & unboxing  (0) 2017.09.08
using 과 Dispose()  (0) 2017.07.19
프로퍼티 value  (0) 2015.11.17
정적 생성자가 호출되는 시점  (0) 2015.11.14
AssemblyInfo.cs  (0) 2015.11.11
반응형


프로퍼티 value


키워드 value는 일반 속성 선언의 set 접근자에 사용되며, 메서드의 입력 매개 변수와 비슷합니다. 단어 value는 클라이언트 코드에서 속성에 할당하는 값을 참조합니다. 다음 예제에서 MyDerivedClass에는 Name이라는 속성이 있습니다. 이 속성은 value 매개 변수를 사용하여 지원 필드 name에 새 문자열을 할당합니다. 클라이언트 코드 측면에서 연산은 단순 할당으로 작성됩니다.

C#
class MyBaseClass
{
    // virtual auto-implemented property. Overrides can only
    // provide specialized behavior if they implement get and set accessors.
    public virtual string Name { get; set; }

    // ordinary virtual property with backing field
    private int num;
    public virtual int Number
    {
        get { return num; }
        set { num = value; }
    }
}


class MyDerivedClass : MyBaseClass
{
    private string name;

   // Override auto-implemented property with ordinary property
   // to provide specialized accessor behavior.
    public override string Name
    {
        get
        {
            return name;
        }
        set
        {
            if (value != String.Empty)
            {
                name = value;
            }
            else
            {
                name = "Unknown";
            }
        }
    }

}


https://msdn.microsoft.com/library/a1khb4f8(v=vs.110).aspx

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

using 과 Dispose()  (0) 2017.07.19
확장메서드 public static class  (0) 2017.07.18
정적 생성자가 호출되는 시점  (0) 2015.11.14
AssemblyInfo.cs  (0) 2015.11.11
객체 배열 new , new  (0) 2015.11.11
반응형
  • 정적 생성자는 액세스 한정자를 사용하지 않고 매개 변수를 갖지 않습니다.

    • 정적 생성자는 첫 번째 인스턴스가 만들어지기 전이나 정적 멤버가 참조되기 전에 클래스를 초기화하기 위해 자동으로 호출됩니다.

    • 정적 생성자는 직접 호출할 수 없습니다.

    • 사용자는 프로그램에서 정적 생성자가 실행되는 시기를 제어할 수 없습니다.

    • 정적 생성자는 일반적으로 클래스에서 로그 파일을 사용할 때 이 파일에 항목을 쓰기 위해 사용됩니다.

    • 정적 생성자는 생성자에서 LoadLibrary 메서드를 호출할 수 있는 경우 비관리 코드에 대한 래퍼 클래스를 만들 때도 유용합니다.

    • 정적 생성자에서 예외를 throw하면 런타임은 다시 이 생성자를 호출하지 않으므로 해당 형식은 프로그램이 실행되는 응용 프로그램 도메인의 수명 동안 초기화되지 않은 상태로 남아 있게 됩니다.

  class test

    {

        public static test tt;

        

        static test()

        {

            Console.WriteLine("정적 생성자");

            tt = new test();

        }

        public test()

        {

            Console.WriteLine("생성자");

        }

    }


    class Program

    {

        static void Main(string[] args)

        {


1.  test asdf1 = new test();        //  기본 생성자가 불리기 이전에 static test() 정적 생성자가 한번 불린다


또는


2. test asdf = test.tt;  // 위 1. 구분을 주석처리했을때는 이 처럼 정적멤버 tt 가 참조 되기 이전에 static test()

 가 호출된다




reference

https://msdn.microsoft.com/ko-kr/library/windows/apps/xaml/k9x6w0hc(v=vs.100).aspx



반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

확장메서드 public static class  (0) 2017.07.18
프로퍼티 value  (0) 2015.11.17
AssemblyInfo.cs  (0) 2015.11.11
객체 배열 new , new  (0) 2015.11.11
추상함수(abstract)  (0) 2015.11.11
반응형

AssemblyInfo.cs

이 파일은 프로젝트와 관계되는 어셈블리의 정보가 담겨 있는 CS 파일입니다. 어셈블리는 하나의 기능을 생성하는 타입과 리소스의 집합체입니다. 하나의 프로젝트를 만든다는 것은 하나의 어셈블리를 만드는 것을 의미합니다.

 

[assembly: ...]는 어셈블리의 정보를 설정하기 위해 사용됩니다. AssemblyInfo.cs에서 설정되는 내용들은 다음과 같습니다.

 

어셈블리 항목

설명 

AssemblyTitle 어셈블리의 애칭(friendly name)
AssemblyDescription 어셈블리의 정보를 간략히 설명
AssemblyConfiguration 
AssemblyCompany 어셈블리를 출하(ship)하는 회사의 이름
AssemblyProduct 제품의 이름
AssemblyCopyright 어셈블리의 저작권 정보
AssemblyTrademark 트레이드마크 정보
AssemblyCulture 어셈블리의 대상 문화권을 의미하는 일람표(enumeration)
ComVisible 이 어셈블리를 COM 컴포넌트들에 노출할지를 결정
Guid 어셈블리의 GUID
AssemblyVersion 문자열로 표현된 버전 정보
AssemblyFileVersion

 Win32 파일 버전을 명시하는 문자열. 기본적으로 AssemlyVersion과

 동일하게 설정

 

프로젝트를 빌드하여 실행 파일을 만든 후 탐색기를 통해 어셈블리의 정보를 확인할 수 있습니다. 방법은 다음과 같습니다.

 

1. 탐색기를 실행한 후 프로젝트 폴더로 갑니다.

2. bin 폴더를 열고, Release 폴더를 선택합니다.

 

 

3. 실행 파일의 아이콘을 마우스 오른쪽 버튼으로 누릅니다.

4. 팝업 메뉴의 최하단에 있는 속성을 택합니다.

 

 

5. 속성 대화상자가 뜬 후 '자세히' 탭을 선택합니다.

6. 5를 실행한 후 나타나는 테이블이 어셈블리 정보의 일부를 보여 줍니다.

 

 

어셈블리 정보를 보여주는 위 그림에서 속성들이 어떤 어셈블리 항목들과 관계되는지 아래 표로 정리합니다.

 

속성

어셈블리 속성 

파일 설명

AssemblyTitle

파일 버전

AssemblyFileVersion

제품 이름

AssemblyProduct

제품 버전

AssemblyFileVersion

저작권

AssemblyCopyright



http://netrance.blog.me/110091707935


반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

프로퍼티 value  (0) 2015.11.17
정적 생성자가 호출되는 시점  (0) 2015.11.14
객체 배열 new , new  (0) 2015.11.11
추상함수(abstract)  (0) 2015.11.11
GetHashCode()  (0) 2015.11.11
반응형
클래스의 객체 또한 배열로 생성할 수 있다. 하지만 값 타입과 차이점이 있기 때문에 정확하게 짚고 넘어가자. 값 타입 배열을 만들면 메모리가 배열의 선언과 동시에 생성된다. 하지만 객체 배열을 만들면 객체의 이름만 첨자 수 만큼 만들어 질 뿐 객체 자체의 메모리는 생성되지 않는다. 즉 객체의 메모리는 따로 생성해주어야 한다. using System; class Animal { private String kind; private String name; public Animal(String kind, String name) { this.kind = kind; this.name = name; } public override string ToString() { return kind + " : " + name; } } public class ObjectArrayTest01 { static void Main() { Animal[] a = new Animal[3]; for (int i = 0; i < a.Length; i++) { try { Console.WriteLine("Animal[" + i + "]:" + a[i] + ", HashCode:" + a[i].GetHashCode()); } catch (NullReferenceException e) { Console.WriteLine(e); } } } } Animal 클래스형의 객체 배열을 생성하고, for문을 사용해서 배열 요소들의 해시코드를 출력한다. 이때 NullReferenceException엘에러가 발생한다. 이것은 null 객체를 참조했기 때문에 발생하는 에러이다. 메모리가 생성되지 않은 상태에서 객체를 참조하기 때문이다. 해시코드는 메모리가 생성될 때 객체에 부여되는 32비트 정수값으로 서로 다른 객체는 같은 해시코드를 부여받을 수 없다. using System; class Animal { private String kind; private String name; public Animal(String kind, String name) { this.kind = kind; this.name = name; } public override string ToString() { return kind + ":" + name; } } public class ObjectArrayTest02 { static void Main() { Animal[] a = new Animal[3]; a[0] = new Animal("개", "진돌이"); a[1] = new Animal("고양이", "나비"); a[2] = new Animal("돼지", "꿀꿀이"); for (int i = 0; i < a.Length; i++) { Console.WriteLine("Animal[" + i + "]:" + a[i] + ", HashCode:" + a[i].GetHashCode()); } } } 각각의 배열 요소에 new 연산자를 사용해서 객체를 생성하고 해시코드를 출력하였다. 이번에는 에러가 발생하지 않고 각각의 배열요소가 해시콜드를 출력한다. 객체 배열은 같은 클래스 타입의 객체를 여러개 생성할 때 사용한다. 따라서 그 배열 요소들이 하나의 객체이기 때문에 일반 클래스를 생성할 때처럼 new 연산자를 사용해서 메모리를 생성해야 한다. 객체 배열은 배열의 생성과 메모리의 할당이 분리되어 있다 http://blog.naver.com/khagaa/30097427182
반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

정적 생성자가 호출되는 시점  (0) 2015.11.14
AssemblyInfo.cs  (0) 2015.11.11
추상함수(abstract)  (0) 2015.11.11
GetHashCode()  (0) 2015.11.11
C# Linq 1강 - Linq 맛보기  (0) 2015.09.10
반응형
// 추상함수(abstract 를 쓴, c++ 로 치면 순수 가상함수)가 하나라도 있는 클래스는
// 추상클래스(class 앞에 abstract 를 붙인)가 되어야 한다.

// cf) abstract 함수나 virtual 함수를 override 할 때 접근 제한자는 바꿀 수 없으며
// 생각해 보면 당연하겠지만, 추상/가상 함수는 protected 와 public 만 가능하다.
// 또한 추상클래스를 상속받은 경우 모든 추상함수를 '반드시' override 해야만 한다.

abstract class Emotion
{
 public float deep;

 // 추상함수
 protected abstract void Feel();

 public virtual void Express()
 {
 }
}

class Pleasure : Emotion
{
 protected override void Feel()
 {
 }

 public override void Express()
 {
 }
}


http://blog.naver.com/herbbread/220000818153


반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

AssemblyInfo.cs  (0) 2015.11.11
객체 배열 new , new  (0) 2015.11.11
GetHashCode()  (0) 2015.11.11
C# Linq 1강 - Linq 맛보기  (0) 2015.09.10
c# XML 문서 주석(C# 프로그래밍 가이드) /// <summary>  (0) 2015.09.02
반응형

http://blog.naver.com/rh0969/166432974


<< GetHashCode() >>
 - 객체를 식별하는 고유한 ID
 - GetHashCode()에 의해서 반환
 - 사용자가 GetHashCode()를 재정의해서 사용할 수 있음


    class HashBase
    {
        private int hash;
        public HashBase(int h)
        {
            hash = h;
        }
    }

    class HashTest
    {
        public static void Main()
        {
            HashBase h1 = new HashBase(10);
            HashBase h2 = new HashBase(10);

            Object obj = new Object();
            string s = "hahaha";
            string t = "hahaha";
            int i = 10000;
            int j = 10000;
            Console.WriteLine("클래스 객체 h1 => " + h1.GetHashCode());
            Console.WriteLine("클래스 객체 h2 => " + h2.GetHashCode());
            Console.WriteLine("Object 객체 obj => " + obj.GetHashCode());
            Console.WriteLine("string 변수 s => " + s.GetHashCode());
            Console.WriteLine("string 변수 t => " + t.GetHashCode());
            Console.WriteLine("int형 변수 i => " + i.GetHashCode());
            Console.WriteLine("int형 변수 j => " + j.GetHashCode());
        }
    }

[출처] [C#]GetHashCode()|작성자 착한남자



반응형
반응형

http://rintiantta.blog.me/40114652444


Console 프로젝트를 만들어줍니다 ㅎㅎ 

 


 

우선 ㅇㅅㅇ, int 로 리스트를 만들어주었습니다. ㅎㅎ


 

만약 여기서 ㅇㅅㅇ, 30 이상인 녀석들만 뽑겠다면 아래와 같이 해야겠지요 ?
 

 

이렇게 걸리적 거리게 무엇을 하는 것을 막고자 ㅇㅅㅇ

무언가를 더 짧고 명료하게 쓰고자, Linq 라는 녀석이 나온 것이랍니다. ㅎㅎ

 

SQL 과 비슷한 문장입니다.

 

우선 살펴보죠 ㅇㅅㅇ, from A in B 는 foreach 와 비슷한 녀석입니다.

반복을 돌릴 것인데, B 에 있는 녀석을 A 에 넣고 그 녀석을 돌릴 거라는 이야기지요 ㅎㅎ

where 은 다음에 보구요 ㅇㅅㅇ, select 는 그렇게 반복이 된 것중에서 우리가 원하는 것을 뺀다는 얘기입니다.

 

아래의 경우 list 에 있는 숫자들이 하나하나 item 에 들어가서 30이 넘는지 확인을 하고

하나하나 item 으로 나가면서 IEnumerator 가 만들어 진답니다.

 

 

결과적으로 ㅇㅅㅇ, numQuery 라는 곳에는 list 중 30이 넘는 녀석들이 다시 배열로 만들어지겠죠.

그럼 아래와 같이 foreach 를 사용해서 출력을 해보겠습니다.
 

 

하고나니 느낀건데.. 30 이하가 애초부터 없었다능 ㅇㅅㅇ...

어찌하였거나, Linq 라고 하는 것은 foreach 와 if 등을 사용해 모두 구현이 가능하지만

Linq 를 사용하면 훨씬 짧게 구현이 가능하답니다 >_<

 

또한 XML 과 SQL 과 연동되어 더욱더 강력한 성능을 발휘하는 Linq..!

차근차근 알아보도록 합시다 ㅎㅎ
 

 
끝 ~


반응형
반응형
https://msdn.microsoft.com/ko-kr/windows/apps/b2s063f7

XML 문서 주석(C# 프로그래밍 가이드)

Visual C#에서는 주석이 참조하는 코드 블록 바로 앞의 소스 코드의 특별 주석 필드(세 개의 슬래시로 표시)에 XML 요소를 포함하여 코드에 대한 문서를 만들 수 있습니다. 예를 들면 다음과 같습니다.

/// <summary>
///  This class performs an important function.
/// </summary>
public class MyClass{}

/doc 옵션을 사용하여 컴파일하는 경우 컴파일러는 소스 코드에서 모든 XML 태그를 검색하고 XML 문서 파일을 만듭니다. 컴파일러에서 생성한 파일을 기반으로 최종 문서를 만들려면 사용자 지정 도구를 만들거나 Sandcastle과 같은 도구를 사용하면 됩니다.

XML 요소를 참조하려면(예를 들어, 함수가 XML 문서 주석에서 설명하려는 특정 XML 요소를 처리하는 경우) 표준 인용 메커니즘(&lt;&gt;)을 사용할 수 있습니다. 코드 참조(cref) 요소에서 제네릭 식별자를 참조하려면 이스케이프 문자(예: cref=”List&lt;T>”) 또는 괄호(cref=”List{T}”)를 사용할 수 있습니다. 특별한 경우로, 컴파일러는 제네릭 식별자를 참조할 때 문서 주석을 더 쉽게 작성할 수 있도록 괄호를 꺾쇠 괄호로 구문 분석합니다.

참고참고

XML 문서 주석은 메타데이터가 아닙니다. 이러한 주석은 컴파일된 어셈블리에 포함되지 않으므로 리플렉션을 통해 액세스할 수 없습니다.


반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

GetHashCode()  (0) 2015.11.11
C# Linq 1강 - Linq 맛보기  (0) 2015.09.10
[C#] 해시테이블 vs 딕셔너리 ( hashtable vs. dictionary ) |  (0) 2015.09.02
C# queue  (0) 2015.09.02
c# Stack  (0) 2015.09.02
반응형

http://hongjinhyeon.tistory.com/87


C#에서는 KEY 와 VALUE를 사용해서 자료를 저장하는 타입이 2가지가 있습니다.

해시테이블과 딕셔너리인데 사용법은 거의 동일하지만 내부적으로 처리하는 기술이 다릅니다.

이 두가지 타입의 기본적인 사용법과 장단점에 대해서 알아보겠습니다.


1.해시테이블 ( Hashtable)


01.//생성
02.Hashtable hashtable = new Hashtable();
03. 
04.//자료 추가 ( 박싱이 일어남)
05.hashtable.Add("Data1"new HongsClass() { Name = "홍진현1", intCount = 1 });
06.hashtable.Add("Data2"new HongsClass() { Name = "홍진현2", intCount = 2 });
07. 
08.//자료 검색
09.if (hashtable.ContainsKey("Data1").Equals(true))
10.{
11.HongsClass temp = hashtable["Data1"as HongsClass; (언박싱 처리)
12.Console.WriteLine(temp.Name);
13.}
14. 
15.//Loop 전체 순회출력
16.foreach (string NowKey in hashtable.Keys)
17.{
18.HongsClass temp = hashtable[NowKey] as HongsClass;
19.Console.WriteLine(temp.Name);
20. 
21.}
22. 
23.//결과  OUTPUT
24.//홍진현1
25.//홍진현1
26.//홍진현2
27. 
28.<p></p>


[해시테이블의 특징]


1.Non-Generic

2.Key 와 Value 모두 Object를 입력받는다.

3.박싱/언박싱(Boxing/Un-Boxing) (참고: http://hongjinhyeon.tistory.com/90을 사용한다. 



즉, 제네릭을 이용하지 않고 Object를 사용하기 때문에 모든 데이터 타입을 다 받고 처리 할 수 있는 장점이 있지만

자료의 입력에 내부적으로 박싱이 일어나고 사용하는 곳에서도 다시 언박싱을 해줘야 사용이 가능합니다.



2.딕셔너리 ( Dictionary )


01.//생성- 제네릭 기반
02.Dictionary<string, HongsClass> dictionary = new Dictionary<string, HongsClass>();
03. 
04.//자료추가
05.dictionary.Add("Data1"new HongsClass() { Name = "홍진현1", intCount = 1 });
06.dictionary.Add("Data2"new HongsClass() { Name = "홍진현2", intCount = 2 });
07. 
08.//자료검색
09.if (dictionary.ContainsKey("Data1").Equals(true))
10.{
11.Console.WriteLine(dictionary["Data1"].Name);
12.}
13. 
14.//Loop 전체 순회출력
15.foreach (HongsClass NowData in dictionary.Values)
16.{
17.Console.WriteLine(NowData.Name);
18.}
19. 
20.//결과
21.//홍진현1
22.//홍진현1
23.//홍진현2

[딕셔너리의 특징]



1.Generic
2.Key와 Value 모두 Strong Type을 입력받는다.(선언시 타입을 입력해줘야함)

3.박싱/언박싱이 일어나지 않는다.



+딕셔너리는 선언시에 사용할 타입을 미리 설정하기 때문에 입력시나 출력시에도 박싱/언박싱이 일어나지 않습니다.

따라서 입력과 사용에 용이하며, 외부에서도 이 타입을 사용할 때도 타입이 정의되어 있으니 다른 타입으로 형변환을

시도하다가 실패할 염려가 없습니다.



3.결론


+두가지 타입이 사용법은 비슷하지만 내부적인 처리와 수용하는 타입의 형태가 다르므로 필요에 따라서 선택을 해야합니다.

고정적으로 하나의 타입만 입력받을 시에는 딕셔너리를 사용하며, Value에 일정한 형식이 없고 여러 형태를 저장하려면

해시테이블을 사용해야합니다.



반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

C# Linq 1강 - Linq 맛보기  (0) 2015.09.10
c# XML 문서 주석(C# 프로그래밍 가이드) /// <summary>  (0) 2015.09.02
C# queue  (0) 2015.09.02
c# Stack  (0) 2015.09.02
C# - Yield  (0) 2015.09.02
반응형

http://rintiantta.blog.me/40115267806



프로젝트를 만듭니다. ㅎㅎ 
 


 

똑같습니다. Queue 를 만드시면됩니다. ㅇㅅㅇ

 

 

종이컵 리필할때는 Enqueue() 를 사용하시면 댑니다 ~_~

 

 

반대로, 빼낼때는 Dequeue() 를 쓰시면 됩니다. ㅎㅎ

 

 

결과입니다. ~_~

 

 

아래와 같이 foreach 로 하시면요 ㅇㅅㅇ...

 

 

내용물을 볼 수는 있어도 실질적으로 종이컵이 빠지는 것이 아닙니다.

(저번 Stack 도 마찬가지 ㅇㅅㅇ..)



 

아래와 같이 Dequeue() 를 쓰시면 내용물도 뺄 수 있답니다. ㅎㅎ

(뺀다는 것이 get 을 의미하는 것이 아니에요. 해당 컬렉션에서 실질적으로 없애버린다는 겁니다.)

 

 

결과입니다. >ㅅ< 



 

역시나, Stack 과 마찬가지로 내용물이 없는데 계속 빼려고 하면요.

 

 

익셉션이 뜹니다. ㅎㅎ...

 

 
그럼 오늘 강의도 끝 ~_~


반응형
반응형

http://rintiantta.blog.me/40115267727




자, 배고프지만 시작합시다 ㅎㅎ...!

프로젝트를 만듭니다. ㅇㅅㅇ ... ! 
 


 

오늘부터 중급강의에서 볼 내용은 Stack 입니다.

Stack 은 아래와 같이 만듭니다. ㅇㅅㅇ ...!

스택에란 스택스택 쌓아두는 곳이지요. (스테이크 쌓듯이 ㅎㅎ)



 

오늘 이대를 다녀왔는데 거기 떡갈비 디게 맛있어요 ㅎㅎ...

떡갈비를 아주머니가 만드시면 타지 않게 앞에 올려두죠 ?

 

   ㅁ

   ㅁ

   ㅁ

   ㅁ

-------

위의 떡갈비(...) 4개 중에서 어떤게 가장 먼저 만들어진 걸까요?

"맨 아래꺼요 >ㅆ<"

『넴 ㅎㅎ, 그렇습니다. ㅇㅅㅇ...! 그리고 아주머니가 파실 때는 윗쪽 것부터 훅훅 파시지요 ㅇㅅㅇ... 그게 Stack 입니다 >ㅆ<』

 

아래와 같이 객체를 만들어서, 스택스택 쌓아두시면 됩니다.



 

아래와 같이 출력이 가능하지만요 ㅇㅅㅇ..

이럼 List 와 다를 빠가 없지요 =_= ...

 

 

어쨌건 출력입니다. ㅇㅅㅇ...

"List와 다를바가 있기는 한데요 ... ?"

『역으로 출력되요 ㅇㅅㅇ... 떡갈비 윗쪽 부터 훑는 것이지요 ㅎㅎ』



 

근데 떡갈비를 보기만 하면 아주머니가 화내시겠죠..

팔아서 없애야죠 ㅇㅅㅇ !!!


아래와 같이 Pop 을 사용해서 없애주시면 됩니다. ㅎㅎ

 


 

결과입니다. ㅇㅅㅇ ... !

 

 

스택에서 주의하실 것은 아래와 같이 팔거 없는데

계속 팔라고하면

 

 

익셉션 뜹니다. ~_~

주의해 주세요 ㅎㅎ...



 
그럼 오늘 강의 끝 >ㅅ<

Stack 과 같은 녀석을 잘 쓰지 않지요 ㅇㅅㅇ..

 

근데, 아이폰 공부하신 분은 아시겠지만 하다보면 Push() 와 Pop() 이 나옵니다.

직접적으로 관련은 없어도, 다른 것을 이해하는데 도움을 줄 수 있는 부분입니다. ㅎㅎ


반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

[C#] 해시테이블 vs 딕셔너리 ( hashtable vs. dictionary ) |  (0) 2015.09.02
C# queue  (0) 2015.09.02
C# - Yield  (0) 2015.09.02
C# | DLL 만들고 사용하기  (0) 2015.09.02
리플렉션(refelction) 활용  (0) 2015.09.01
반응형

http://rintiantta.blog.me/40113858529



Yield 라는 키워드는 Break 와 Return 과 함께 쓰인답니다.

음.. Break 는 자주 활용이 되지는 않는 것 같습니다만, 강의이니 뭐 알아봐야죠 ㅎ..! 

 

간단하게, Yield 라는 키워드는 일반적인 것을 IEnumerable 로 변환을 시켜줍니다..

뭔소린지, 해보면서 봅시다..!

 

우선 프로젝트를 만듭니다. 이름은 EnumYieldLesson 입니다..!

 

 

원래, 강의중에 클래스를 따로 만드는 타입이잖습니까..

일반 교재에는 다 합치지만.. 실제로 안 그러니... 연습도 되시라구 하는데...

오늘건 나누면 조금 길어지는 관계로 합쳤습니다... 죄송합니다 =_=...

 

어찌하였거나, YieldTest 라는 이름의 클래스를 만들어주었습니다 ㅇㅅㅇ..!

 

 

그리고 아래와 같이 days 라는 이름으로 만들어주었습니다.

참고로, MSDN 에 있는 내용인데요. 음... 그냥 Yield Return 과 GetEnumerator 에 대한 설명밖에 없길래

추가하고 합쳐보았습니다. ㅎㅎ.... (MSDN은 저작권이.. 교육용으로는 안 걸리지...)

 

 

다음과 같은 IEnumerator 로 리턴하는 GetEnumerator라는 메소드를 만듭니다.

using 하라는 말이 뜨니 using 해줍시다.

 

참고로, 이름을 바꿔서 예제를 따라해보는 분들이 계실텐데요. (제가 그런 타입이라..)

GetEnumerator 라는 이름은 고정하셔야 합니다. 잠시후 뒤에서 언급할게요.

 

 

그리고 아래와 같이 메소드를 작성했는데요.

일단 리턴값이 IEnumerator 고, 리턴시 yield 라는 것을 쓴 것을 보실 수 있답니다.

리턴이 분명 string 으로 될텐데, yield 라는 키워드를 붙임으로써, IEnumerable 로 바뀌게 됩니다.

"어라... 리턴이 IEnumerator 인테.. 왜... IEnumerable.. ㅇㅅㅇ ... ?"

『잠시후 봅시다...ㅎ』

 

 

근데 왜 이름이 고정이냐면요 ㅇㅅㅇ

원래 IEnumerable 인터페이스의 상속을 받아서 구현해야합니다. ㅇㅅㅇ

 

 

인터페이스 구현버튼을 누르시면 아래와 같이 GetEnumerator 라는 메소드가 구현되는데요.

원래는 이렇게 해서 해야하지만... region 때문에 길이가 길어서 그냥 하고 있습니다...

 

 

어찌하였거나, 이어서 메소드를 만드는데요.

OtherGetEnum() 이라고 만들었습니다. 내부의 for 문을 foreach도, for 도 된다는 뜻으로 해본거구요.

 

주의하실것은.. IEnumerable 로 리턴한다는 것입니다...

"ㅇㅅㅇ...."

 

 

자, 그럼 이제 만들었으니, 사용해봅시다~!

아래와 같이 인스턴스를 만들었답니다.

 

 

그리고 아래와 같이 실행이가능합니다.

그냥 꼽으시면, foreach 에서 알아서 "아, GetEnumerator() 실행하라는 거구나."

하고 GetEnumerator 를 실행합니다.

 

근데 문제가 있습니다. foreach 에는 IEnumerator 값을 꼽을 수 없습니다.

따라서, 내부에서 한번 더 변형이 일어나서 IEnumerable 로 바뀐답니다.

 

 

실행 결과입니다. 간단하죠 ?

 

 

이어서, 아래와 같이 우리가 만든 녀석을 써보았습니다.

위와 실행 결과가 같답니다 ㅎㅎ

 

 

또한 아래와 같이 따로 뺀다음 꼽을 수도 있는 것을 생각하시기 바랍니다.

"오오, 그럼 IEnumerator 로 GetEnumerator 뽑아서 꼽아도 되는거 아닌가요?"

『해보시길 바랍니다. 에러날겁니다. ㅇㅅㅇ... IEnumerator 는 꼽을 수 없다구요 ㅎ...』

 

 

이어서, yield break; 를 보도록 합시다.

아래와 같이 메소드를 수정했는데요.

 

yield break; 는 yield return 을 끝내라는 말입니다.

즉, 지금 한데까지만 IEnumerable 로 바꾸라는 이야기지요.

 

 

그래서 아래와 같이 것을 실행하시면

 

 

이렇게 됩니다. yield return 이 중간에 끊긴 것을 확인하실 수 있습니다.

 

 

 자... 그럼 마지막으로 무언가 하나를 또 보기위해서 AnotherGetEnum 을 만듭니다.

『...복사해서.. AnOtherGetEnum 이 되었다는 설이..』.... 죄송합니다.

 

 

이어서, 다음과 같이 써주었습니다.

"ㅇㅅㅇ ?!"

실행결과를 보시는 편이 이해가 빠를 것이라고 생각됩니다 ㅎㅎ....

 

 

아래와 같이 메인에 써주었어요 ㅇㅅㅇ..!

 

 

자, 출력결과가 보이시나요 ㅇㅅㅇ ?

하나, 하나, 하나 출력되었습니다.

 

이와같이 yield 키워드는 IEnum 계열과 foreach 와 함께쓰이는 녀석입니다.

 

 

그럼 오늘 강의 끝 ~

오늘건 약간 길었지요 ㅇㅅㅇ....?

 

음.. 아직 베이직에서 아직 인터페이스도 안했는데.... 중급강의에서 나오니 조금 그렇지만..

아직 보신 분이 많지 않으니 괜찮을듯 ~ ㅎㅎ;; 


반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

C# queue  (0) 2015.09.02
c# Stack  (0) 2015.09.02
C# | DLL 만들고 사용하기  (0) 2015.09.02
리플렉션(refelction) 활용  (0) 2015.09.01
열거형 GetEnumerator(), IEnumerable, IEnumerator 정의  (0) 2015.09.01
반응형

http://wwwi.tistory.com/316


C#에서 DLL만들고 사용하기

개발환경은 Visual C# 2010 Express에서의 예제이다.
이 내용은 Visual Studio 2010에서 해도 문제가 없다.

메뉴 표시는 대신 한글이여서 조금은 이해하기 편할 것 같다.


먼저 DLL을 사용할 프로젝트를 만든다.
먼저 File > New Project를 선택한다.

Console Application을 선택하고 적당한 이름을 입력한 후 OK를 누른다.
여기서 입력한 이름으로 프로젝트가 만들어지고 이 이름의 네임스페이스가 생긴다.

그러면 아래와 같이 자동으로 소스가 조금 만들어진다.


사용할 DLL 프로젝트 만들기

솔루션에서 왼쪽 클릭을 하고 Add > New Project를 선택한다.



Class Library를 선택하고 DLL의 이름을 적고 OK를 누른다.


DLL에 대한 내용을 적어둔다.



using System;

namespace MyCompo
{
    public class Calcurate
    {
        public int Add(int a, int b)
        {
            return (a + b);
        }
    }
}


DLL을 Build한다.


작성된 DLL을 참조하기 위해서는 DLL을 사용하려는 프로젝트의 References(참조)에서 오른쪽 클릭을 하여 Add Reference를 선택한다.


그리고 Projects 탭을 선택하면 지금 작성한 DLL의 프로젝트를 선택하고 OK를 누른다.

정상적으로 Dll이 참조되면 References 밑에 추가한 DLL의 이름이 표시된다.

그리고 아래와 같이 코드를 작성한다.


using System;
// DLL Namespace 추가
using MyCompo;

namespace UseDll
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 10;
            int b = 20;
            int c = 0;

            // DLL에 정의된 클래스로 객체 정의하기
            Calcurate cal = new Calcurate();

            // DLL의 메서드 사용하기
            c = cal.Add(a,b);

            Console.WriteLine("{0} + {1} = {2}", a, b, c);
            Console.ReadLine(); 
        }
    }
}

그리고 실행하면 아래와 같은 결과가 표시된다.


반응형
반응형
http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=17&MAEULNo=8&no=33805&ref=33729


간단히 말하면 런타임으로 동적으로 다양한 작업을 하고자 할 때 사용합니다.

 

1) 다음과 같이 메타 정보를 런타임에 얻고자 할 경우

 

메타 정보란 응용프로그램의 기능와 상관없는 정보

=> 실제 작업코드가 아닌 정보들...

=> 그 파일에 따라다니는 파일과 관련된 정보들

 

private string FileVersion()

{

    Assembly asm=Assembly.LoadFrom(@"C:\abc.exe");

    AssemblyName name=asm.GetName();

 

    return name.Version.ToString();

}

 

라이브러리를 개발하면서 유지보수 내용을 기록하고 이런 메타 정보를 얻어서 유지보수등에 도움을 받기도 하고...

 

2) 어셈블리의 내용을 알고자 할 때

이벤트, 필드, 메소드 등등

 

Assembly asm = Assembly.Load("Mscorlib.dll");

Type[] types = asm.GetTypes();

foreach(Type t in types)

{

    Console.WriteLine("Type : {0}", t)

}

 

3) 개체의 이름으로 개체의 인스턴스 만들기

=> 융통성 있는 코드를 작성할 수 있습니다.

 

예를 들어

다음과 같이 하면 오직 Form1만 열게 되고

private void OpenForm1()

{

    Form frm = new Form1();

    frm.Show();

}

 

다음과 같이 switch 구문을 사용해도 폼이 추가되면 또 코드를 수정해야 되고

private void OpenForm(string formName)

{

    Form frm = null;

 

    switch(formName)

    {

        case "Form1":

            frm = new Form1;

            break;

        case "Form2":

            frm = new Form2;

            break;

        ....

    }

    frm.Show();

}

 

그러나 리플렉션을 이용하면...

  

private button1_Click(.....)

{

    OpenFormByName(this.textBox1.Text);

}

 

private void OpenFormByName(string formName)

    Assembly am = Assembly.GetEntryAssembly();

    string fullName = asm.ToString() + "." + formName;

 

    // 리플렉션: 이름(문자정보)으로 해당 타입을 알아내는 것

    // => 이름을 주고 '요놈이 누구인가 찾아주세요' 하면 심사숙고해서 (리플렉션) 결과를 찾아주는 것.

    Type t = Type.GetType( fullName );

 

    // 후기 바인딩: 타입을 가지고 인스턴스를 만들고

    object o = Activator.CreateInstance(t);

 

    // 폼을 보여 줌

    Form f = (Form) o;

    f.Show();

}

 

 

또 이렇게 폼을 리플렉션을 이용하여 작업을 하게되면

폼을 독립된 DLL 파일로 개발하고

폼을 업데이트 하는 경우 해당 DLL 파일만 업데이트 하면 되고....

 

4) COM 개체를 후기바인딩으로 불러 사용

 

private void OpenExcel()

{

    // 리플렉션

    Type excel = Type.GetTypeFromProgID("Excel.Microsoft");

 

    // 후기바인딩

    object objExcel = Activator.CreateInstance(excel)

    object[] param = object[1];

    param[0]=true;

    excel.InvokeMember("Visible", BindingFlags.SerProperty, null, objExcel, param);

}

 

이렇듯 런타임에 동적으로(융통성있게) 개체를 가지고 작업을 할 수 있습니다.

 

후기바인딩으로 인스턴스를 만드는데 쓰이는

Activator의 메소드들은 다음과 같습니다.

 

    CreateComInstanceFrom()

    CreateInstanceFrom()

    GetObject()

    CreateInstance()

 

도움말을 한번 찾아보십시오.

이것을 알면 리플렉션을 어떻게 사용할 것인가에 대해 좀더 알 수도 있을 겁니다.

 

5) 런타임으로 스크립트를 작성하고 실행하거나 컴파일을 하거나 새로운 타입을 동적으로 생성하는 등

고급 작업을 할 수 있습니다.

기타 VS.NET의 IDE와 같은 것을 만들 수도 있겠습니다.

이 부분은 나중에 개념 잡으시면서 접해보도록 하십시오.



반응형
반응형


using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace TestE
{

    class User
    {
        public User(string name) { _name = name; }
        public string Name{ getreturn _name;} }
        string _name;
    }


    class DBManager : IEnumerable
    {
        public DBManager()
        {
            _onlineUsers.Add (new User ("tom"));
            _onlineUsers.Add (new User ("Krista"));
            _onlineUsers.Add (new User ("Emma"));
        }

        public IEnumerator GetEnumerator()                //이 함수를 불릴때 원소를 복사하는 방식 => 메모리 측면에서 효율적인 방법은 아님
        {
            return new Enumerator (_onlineUsers);
        }
            
        ArrayList _onlineUsers = new ArrayList();

        class Enumerator : IEnumerator
        {
            const string INVALID_RECORD = "use ss";

            public Enumerator(ArrayList onlineUsers)
            {
                foreach(User user in onlineUsers)
                {
                    this._onlineUsers.Add(user);
                }
                Reset();
            }

            public bool MoveNext()
            {
                return ++_index < _onlineUsers.Count;
            }

            public object Current
            {
                get
                {
                    if (_index == -1
                        throw new InvalidOperationException (INVALID_RECORD);

                    if (_index < _onlineUsers.Count)
                        return _onlineUsers [_index];
                    else
                        return null;
                    
                }
            }

            public void Reset(){ _index = -1; }

            int _index;
            ArrayList _onlineUsers = new ArrayList();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {

            DBManager db = new DBManager ();

            IEnumerator e = db.GetEnumerator ();

            while (e.MoveNext ()) {
                User user = (User)e.Current;
                Console.WriteLine ("{0}", user.Name);
            }


            foreach (User user in db) {
                Console.WriteLine ("{0}", user.Name);
            }

            Console.ReadLine ();
        }
    }


}

반응형
반응형

http://www.gpgstudy.com/forum/viewtopic.php?p=95504&sid=3186560b92d188fe3fab7bef907f0c67

delegate하고 event하고 뭐가 다른지 잘 몰라서 그러는데요. 


class Test
{
    public delegate void Handler(string msg);
 
    public event Handler Event;
    public Handler Delegate;
 
    public void DoIt()
    {
        Event("event");
        Delegate("delegate");
    }
};
 
class Program
{
    static void MyFunc(string caller)
    {
        Console.WriteLine("Message from {0}", caller);
    }
     
    static void Main(string[] args)
    {
        Test test = new Test();
        test.Event += MyFunc;
        test.Delegate += MyFunc;
        test.DoIt();
    }

}

이 코드를 보면 event나 delegate이나 차이가 없어 보이는데 
혹시 제가 모르는 차이점이 있나요?







저도 예전에 차이점이 궁금했었는데요.. 
겉으로 보기에 용법이 같아서 그렇지 event와 delegate는 다른 놈입니다. 

event와 delegate를 
property와 멤버 변수의 관계랑 비슷하다고 생각하시면 됩니다. 

event는 property가 get, set을 갖는 것 처럼 add, remove함수를 가져야 합니다. 
근데 사용하기 쉽게 add, remove를 생략하실수 있습니다. 


public event Handler Event;  // 생략형



그냥 이렇게 하면 아래와 같은 코드가 만들어집니다. 


private Handler _Event; // _Event라는 delegate 변수를 자동으로 만들어냅니다.
public event Handler Event
{
    add
    {
        lock(this) { _Event += value; }
    }
 
    remove
    {
        lock(this) { _Event -= value; }
    }

}


이 Event 이벤트 변수에다가 +=을 써주면 add가 불리고 -=을 써주면 remove가 불리는 겁니다. 
그래서 겉에서 봤을땐 delegate랑 사용법이 똑같이 보이게 됩니다. 

근데 이것도 약간 다른 점이 이벤트 호출시에 Event("event");를 사용하는데 
이건 클래스 안에서만 사용 가능합니다. 클래스 밖에서 test.Event("event"); 하면 에러가 나지요 
생략형으로 쓰면 c#이 자동으로 Event() 호출을 _Event 대리자의 호출로 치환해 줍니다. 
_Event는 내부 변수이므로 밖에서 호출이 불가능합니다. 

add, remove를 명시적으로 쓸 경우에는 Event("event"); 이런식으로 호출을 못합니다(자동으로 대리자 변수를 만들어내지 않기 때문에) 
직접 _Event("event"); 이런식으로 대리자를 호출해 주거나 그에 상응하는 작업을 구현해야 합니다. 

이러한 차이점이 있기 때문에 event는 interface에서 선언해 줄수 있고요.. delegate는 그렇지 못합니다. 

event변수가 넘쳐나는걸 막기 위해 winforms에서는 메시지 이벤트를 add, remove를 직접 구현하고 dictionary에 event를 저장하는 방식을 쓴다고 합니다. 
http://msdn2.micros...ibrary/z4ka55h8.aspx
 

여기 Events 부분에 자세한 설명이 있습니다. 
http://www.yoda.ara...m/csharp/events.html





https://msdn.microsoft.com/ko-kr/library/z4ka55h8.aspx


방법: 사전을 사용하여 이벤트 인스턴스 저장(C# 프로그래밍 가이드)

각 이벤트에 대해 필드를 할당하는 대신 사전을 사용하여 이벤트 인스턴스를 저장하는 방법으로 많은 이벤트를 노출할 때 accessor-declarations를 사용할 수 있습니다. 이 방법은 많은 이벤트를 사용할 수 있지만 대부분의 이벤트가 구현되지 않을 것으로 예상할 때만 유용합니다.

public delegate void EventHandler1(int i);
public delegate void EventHandler2(string s);

public class PropertyEventsSample
{
    private System.Collections.Generic.Dictionary<string, System.Delegate> eventTable;

    public PropertyEventsSample()
    {
        eventTable = new System.Collections.Generic.Dictionary<string, System.Delegate>();
        eventTable.Add("Event1", null);
        eventTable.Add("Event2", null);
    }

    public event EventHandler1 Event1
    {
        add
        {
            lock (eventTable)
            {
                eventTable["Event1"] = (EventHandler1)eventTable["Event1"] + value;
            }
        }
        remove
        {
            lock (eventTable)
            {
                eventTable["Event1"] = (EventHandler1)eventTable["Event1"] - value;
            }
        }
    }

    public event EventHandler2 Event2
    {
        add
        {
            lock (eventTable)
            {
                eventTable["Event2"] = (EventHandler2)eventTable["Event2"] + value;
            }
        }
        remove
        {
            lock (eventTable)
            {
                eventTable["Event2"] = (EventHandler2)eventTable["Event2"] - value;
            }
        }
    }

    internal void RaiseEvent1(int i)
    {
        EventHandler1 handler1;
        if (null != (handler1 = (EventHandler1)eventTable["Event1"]))
        {
            handler1(i);
        }
    }

    internal void RaiseEvent2(string s)
    {
        EventHandler2 handler2;
        if (null != (handler2 = (EventHandler2)eventTable["Event2"]))
        {
            handler2(s);
        }
    }
}

public class TestClass
{
    public static void Delegate1Method(int i)
    {
        System.Console.WriteLine(i);
    }

    public static void Delegate2Method(string s)
    {
        System.Console.WriteLine(s);
    }

    static void Main()
    {
        PropertyEventsSample p = new PropertyEventsSample();

        p.Event1 += new EventHandler1(TestClass.Delegate1Method);
        p.Event1 += new EventHandler1(TestClass.Delegate1Method);
        p.Event1 -= new EventHandler1(TestClass.Delegate1Method);
        p.RaiseEvent1(2);

        p.Event2 += new EventHandler2(TestClass.Delegate2Method);
        p.Event2 += new EventHandler2(TestClass.Delegate2Method);
        p.Event2 -= new EventHandler2(TestClass.Delegate2Method);
        p.RaiseEvent2("TestString");

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

*/

반응형
반응형

using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace TestE
{
    class DBConnection {

        protected static int NextConnectionNbr = 1;

        protected string connectionName;
        public string ConnectionName{ get { return connectionName; } }

        public DBConnection()
        {
            connectionName = "Database Connection " + DBConnection.NextConnectionNbr++;
        }

    }


    class DBManager
    {
        protected ArrayList activeConnetions;
        public DBManager()
        {
            activeConnetions = new ArrayList ();
            for (int i = 0; i < 6; ++i) {
                activeConnetions.Add (new DBConnection ());

            }
        }

        public delegate void EnumConnectionsCallback(DBConnection connection);            

//delegate 선언, 이 함수 타입과 동일한 함수를  나중에 담을 수 있게 된다

        public void EnumConnections(EnumConnectionsCallback callback)                     
        {
            foreach(DBConnection connection in activeConnetions)
            {
                callback(connection);
            }
        }
    }



    class Program {

        public static void PrintConnections(DBConnection connection)
        {
            Console.WriteLine ("[InstanceDelegate.PrintConnections] {0}", connection.ConnectionName);
        }

        static int Main(string[] args)
        {

            DBManager dbMsanager = new DBManager ();

            DBManager.EnumConnectionsCallback printConnections = new DBManager.EnumConnectionsCallback

//delegate 와 동일한 함수 타입의 함수인  PrintConnections 를 담는다

            dbMsanager.EnumConnections (printConnections);

            Console.ReadLine ();

            return 0;
        }
    }
}




결과


[InstanceDelegate.PrintConnections] Database Connection 1

[InstanceDelegate.PrintConnections] Database Connection 2

[InstanceDelegate.PrintConnections] Database Connection 3

[InstanceDelegate.PrintConnections] Database Connection 4

[InstanceDelegate.PrintConnections] Database Connection 5

[InstanceDelegate.PrintConnections] Database Connection 6



- 참고 inside c# second

반응형
반응형
http://ngcbbs.tistory.com/15

단하게 C/C++ 을 알고 있는 사용자에게 설명하면 함수 포인터 같은녀석! 이라고 이야기 할 수 있겠다.

다만 사용 방법이나 다중 위임의 형태를 C/C++ 에서 구현하기 위해서는 잡다한 코드가 더 추가되어야 하지만 정말 간편하게 다중 위임으로 처리가 가능하다.

delegate 키워드를 사용.


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
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
 
namespace ToolboxTestApp1
{
    class Program
    {
        // 위임이 무엇인고??
        public delegate int Printer(string msg); // 함수 포인터 처럼 함수 바디는 필요 없다.
 
        public static int Printer_My1(string msg)
        {
            System.Console.WriteLine("Printer_My1 = " + msg);
            return 1;
        }
 
        public static int Printer_My2(string msg)
        {
            System.Console.WriteLine("Printer_My2 = " + msg);
            return 2;
        }
 
        static void Main(string[] args)
        {
            Printer _printer = new Printer(Printer_My1);
            _printer += new Printer(Printer_My2); // += 연산자를 통해서 다중 위임 처리.
 
            _printer("한가한가~"); // _printer 에 위임된 프린터가 동작한다.
        }
    }
}



반응형
반응형

http://pullthelever.tistory.com/332






- Case 1

class A
{
   b;       //당연히 된다.
   B.c;    //클래스 B 안에 클래스 C가 public이므로 가능하다.
                 //public이 아니면 아래와 같은 오류가 나온다.
                 //오류 CS0122: 보호 수준 때문에 '_namespace.B.C'에 액세스할 수 없습니다.

}

class B
{
    public class C//public말고도 private, protected도 가능하다.
    {
       //...
    }
}



- Case 2
class A          //기본으로 internal이다.
{
   //...
}

public class B//private, protected는 못 오고 internal, public만 올 수 있다.
{
   public a;
// 오류 CS0052: 일관성 없는 액세스 가능성:
// '_namespace.A' 필드 형식이 '_namespace.B.a' 필드보다 액세스하기 어렵습니다.

   public func1() { return a; }
// 오류 CS0050: 일관성 없는 액세스 가능성:
// '_namespace.A' 반환 형식이 '_namespace.B.func1()' 메서드보다 액세스하기 어렵습니다.

   public void func2(a) { this.a = a; }
// 오류 CS0051: 일관성 없는 액세스 가능성:
// '_namespace.A' 매개 변수 형식이 '_namespace.B.func2(_namespace.A)' 메서드보다
//  액세스하기 어렵습니다.
}


- 이유
   A는 internal로 외부로 공개되지 않았는데 외부로 공개하는 B에서 A를 외부로 공개하려고 하니 오류가 발생한다.

- Case 2 처방전
첫 번째 방법)
   class 를 public class A로 바꾼다

두 번째 방법)
   public class B를 class B로 바꾼다

세 번째 방법)
   public a, public func1(), public void func2(a) 
   a, func1(), void func2(a) 로 바꾼다

- public과 internal란
public으로 선언된 경우 다른 어셈블리에서도 접근할 수 있다.
internal으로 선언된 경우 동일한 어셈블리의 파일 내에서만 접근할 수 있다.
아무것도 안 넣으면 internal이다.


반응형
반응형
http://cjh7163.blog.me/220444677611



일반적으로 생성자는 클래스의 인스턴스 필드를 초기화하려고 사용한다.
그렇다면 정적 생성자는 어떤 용도로 사용할까?

당연히 클래스의 정적 필드를 초기화할 때 사용한다. 아래의 예제를 보자.

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
 
using System;
namespace _static_constructor
{
    class Student
    {
        static public int Id { get; private set; }
        public Student()
        {
            Console.WriteLine("Student ID + 1");
            ++Id;
        }
        static Student()
        {
            Console.WriteLine("Set Student ID : 0");
            Id = 0;
        }
    }
    class MyApplication
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Student ID : {0}", Student.Id);
            Student std1 = new Student();
            Student std2 = new Student();
            Console.WriteLine("Student ID : {0}", Student.Id);
        }
    }
}
 
cs

 


위의 소스코드를 보면 생성자가 두 가지가 있는데, 하나는 인스턴스 생성자이고 다른 하나는 정적 생성자이다.

코드는 어떤 동작을 하겠는가?


먼저 Student 클래스의 정적 필드 Id를 출력하고, 뒤에 Student 인스턴스를 두개 생성하여 인스턴스 생성자가 두번 호출되고

Id는 1씩 두번 증가하게 될 것이다. 그리고 마지막으로 Id를 출력한다.


인스턴스 생성자는 클래스 인스턴스가 생성될 때 호출된다. 그렇다면 정적 생성자는 언제 호출될까?


일단 프로그램을 먼저 실행해보겠다.




Student 클래스의 정적 필드 Id를 출력하기 전에 정적 생성자가 호출되어 "Set Student ID : 0" 문자열을 출력하는 것을 볼 수 있다. 그렇다면 프로그램이 시작되면 자동으로 정적 생성자가 호출되는 것일까?

메인 메소드의 가장 윗 부분에 "break"라는 문자열을 출력하도록 코드를 추가하였다.




정적 생성자가 "break" 문자열이 출력된 다음에 호출되었다. 그렇다면 프로그램이 시작되면 자동으로 호출되는 것은 아니라는 것이다. 그러면 무엇에 의하여 정적 생성자가 호출 되는 것일까? 메인 메소드만 떼어내서 올려보겠다.


 

1
2
3
4
5
6
7
8
        static void Main(string[] args)
        {
            Console.WriteLine("break");
            Console.WriteLine("Student ID : {0}", Student.Id); 00
            Student std1 = new Student();
            Student std2 = new Student();
            Console.WriteLine("Student ID : {0}", Student.Id);
        }
cs


3번줄과 4번줄 사이에서 정적생성자가 호출되었다. 3번줄은 단순히 문자열을 출력하는 문장이므로 정적 생성자 호출과 전혀 연관성이 없음을 알 수 있을 것이다. 4번줄을 살펴보자. 여기서 문자열에 클래스의 정적 필드를 출력하는 것을 확인할 수 있다. 

이것으로 정적필드에 접근하려고 하면 먼저 정적 생성자가 생성된다는 것을 알 수 있다.

위의 코드에서 4번줄을 지우면 아래와 같은 결과를 볼 수 있다.




정적 필드에 접근하려고 할 때 뿐만아니라 인스턴스를 생성할 때도 먼저 정적 생성자가 호출된 뒤 인스턴스 생성자가 호출되는 것을 볼 수 있다. 또, 정적 생성자는 클래스 당 단 한번만 호출된다.


정적 생성자(static constructor)에 대하여 정리하면 다음과 같다.


1) 클래스의 정적필드에 접근하기 직전에 호출된다.

2) 클래스의 인스턴스를 생성하기 직전에 호출된다.

3) 모든 클래스의 정적 생성자는 최초 한번만 호출된다.


반응형
반응형

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace TestE
{


    class Employee
    {
        public string _name;
        public Employee(string name)
        {
            this._name = name;
            Console.Write ("Employee.CalculatePay:\n\t");
            CalculatePay ();
        }
        public virtual void CalculatePay()
        {
            Console.WriteLine ("Employee.CalculatePay called for {0}", _name);
        }
    }

    class SalariedEmployee : Employee
    {
        public decimal _salary;    
        public SalariedEmployee(string name, decimal salary) : base(name)
        {
            this._salary = salary;
            Console.Write ("SalariedEmployee.CalculatePay:\n\t");
            CalculatePay ();
        }
        public override void CalculatePay()
        {
            Console.WriteLine ("{0}, SalariedEmployee.CalculatePay, salary={1:C}", _name, _salary);
        }
    }

    class ContractEmployee : Employee
    {
        public double _rate;
        public ContractEmployee(string name, double rate) : base(name)
        {
            this._rate = rate;
            Console.Write ("ContractEmployee.CalculatePay:\n\t");
            this.CalculatePay ();

        }

        public override void CalculatePay()
        {
            Console.WriteLine ("{0}, ContractEmployee.CalculatePay, salary={1:C}", _name, _rate);
        }
    }

    class Program {
        static void Main(string[] args)
        {

            Employee[] employee = new Employee[2];
            employee[0] = new ContractEmployee("Ada" +
                "m Barr"123.45);

            Console.WriteLine ("");


            employee[1] = new SalariedEmployee(
                "Max Benson"67890m);

        }
    }
}



c++ 과는 다른 결과를 보인다, 생성자가 호출된 클래싀의 함수가 불리지 않고 virtual 로 선언된 경우 자식 함수가 불리게 된다




Employee.CalculatePay:

Adam Barr, ContractEmployee.CalculatePay, salary=₩0

ContractEmployee.CalculatePay:

Adam Barr, ContractEmployee.CalculatePay, salary=₩123


Employee.CalculatePay:

Max Benson, SalariedEmployee.CalculatePay, salary=₩0

SalariedEmployee.CalculatePay:

Max Benson, SalariedEmployee.CalculatePay, salary=₩67,890





반응형
반응형
https://kloudstyle.wordpress.com/2012/09/05/c-%EC%83%81%EC%86%8D%EB%B0%9B%EC%9D%80-%ED%9B%84-new-override-%EC%B0%A8%EC%9D%B4%EC%A0%90-%EC%98%88%EC%A0%9C/



[C#] 상속받은 후 new , override 차이점 예제


public class BaseClass

{

    public virtual void Foo() { Console.WriteLine("BaseClass.Foo"); }

}

 

public class Overrider : BaseClass

{

    public override void Foo() { Console.WriteLine("Overrider.Foo"); }

}

 

public class Hider : BaseClass

{

    public new void Foo() { Console.WriteLine("Hider.Foo"); }

}

 

static void Main(string[] args)

{

    Overrider over = new Overrider();

    BaseClass b1 = over;

    over.Foo(); // Override.Foo

    b1.Foo();   // Override.Foo

 

    Hider h = new Hider();

    BaseClass b2 = h;

    h.Foo();    // Hider.Foo

    b2.Foo();   // BaseClass.Foo

}



override 메소드로 구현하게되면 부모클래스에서 호출하면 자식클래스의 오버라이드함수를 수행한다.
new 메소드로 구현하게되면, 기존의 부모와 별개의 함수이므로, 부모클래스에서 호출하면 부모클래스의 원래 함수를 실행하고, 자식클래스에서는 자식클래스의 함수를 실행한다.


출처: C# 5.0 in a nut shell


반응형
반응형

http://soulduo.tistory.com/77


숫자 데이터 형식에 대해 상한 값과 하한 값이 고정되어 있다.

(이는 MaxValue / MinValue 로 확인 가능하다.)

프로그래머의 실수로 상한 값과 하한 값을 범위를 넘겨 값을 활당할 경우

각각 '오버플로우'와 '언더플로우' 상황이 발생한다.

CLR에서는 두 경우 모두 '오버플로우'로 통칭한다.

왜 '가 발생한다'로 표기하지 않고 '상황이 발생한다.' 라고 표현했는지는

기초를 충실히 했다면 간단한 설명으로 이해가 가능할 것이다.

이는 byte의 최대 값이 255이라는 것을 생각하고 다음의 경우를 보자.

byte b1 = 100;
byte b2 = 250;
byte b3 = (byte) b1 + b2;

이 때 b3의 값은 얼마일까?

b3에는 넘치고 남는 값만이 남게 된다. (350 - 256 = 94)

상황이 발생한다는 것은 논리 에러만이 존재하며 예외 상황이 발생하지 않기 때문에

표현한 것이다.

간단한 연산에 대해서는 프로그래머가 모두 관리 가능하겠지만

어째건 모든 프로그래머는 실수를 하기 마련이다.

(존경 받는 '찰스 페졸드' 같은 사람도 어떤 부분에서든 실수를 하고)

(이를 바로 잡을 것이다.)

아무튼 이러한 상황을 프로그래밍 차원에서 규제할 수 있다.

이 때 사용되는 코드가 checked이다.

다음과 같이 코드를 작성하게 되면 오버 플로우 상황에서

System.OverflowException 예외가 발생하게 된다.


try
{

byte b3 = checked((byte)(b1 + b2));
}
catch(OverflowException e)
{
Console.WriteLine(e.Message);
}

비쥬얼 스튜디오의 경우 프로젝트 속성 페이지의 산술 연산 오버플로/언더플로 확인

옵션을 'True'로 설정함으로 프로젝트 단위에서 예외 발생 여부를 확인할 수 있다.

하지만 모든 상황에서 예외 처리를 하고 싶지 않을 때가 있을 것이다.

이럴 때 'unchecked' 키워드를 사용하게 된다.

unchecked
{
byte b3 = (byte)(b1 + b2);
}

모든 오버플로우 체크 중에 다음의 연산에서는 예외 상황을 발생시키지 않으며

(350 - 256 = 94)의 연산을 수행하여 b3는 94의 값을 가지게 된다.


반응형
반응형

http://www.gamedevforever.com/322



C# Boxing과 Unboxing

프로그래밍 2015/01/30 09:43
Posted by 친절한티스

요즘 유니티를 이용해 C#으로 코딩을 하시는 분들이 많은데, 간혹 면접을 보다보면 Boxing과 Unboxing에 대해 제대로 이해하지 못하는 분들이 많더군요. 이를 C/C++의 캐스팅 같은 것으로 생각하고 마구잡이로 사용하시는 분들이 많은듯 합니다. 하지만 C/C++의 캐스팅과 다르게 C#의 Boxing은 그 과정에서 개체 할당이 일어 나기 때문에 성능 이슈가 생길수 있습니다.


  1. int num1 = 123;  
  2. object obj = num1;      // boxing  
  3. int num2 = (int)obj;    // unboxing  


C#에서 사용되는 모든 타입들은 object 에서 직/간접적으로 상속을 받게 됩니다. 즉, 최상위 부모 클래스 같은 존재지요. 이것이 C/C++에서 였다면, 위의 num1에서 obj로의 대입은 단순 업캐스팅으로 변환되는 거기 때문에 큰 문제는 없을 겁니다. 하지만 C#에서는 다릅니다. C#에서는 이를 Boxing이라 부르며 아래와 같이 힙 Heap에 새로운 개체를 할당하고 그 곳에 값을 복사하게 됩니다. 



C++ 코드로 보자면 아래와 같은 상황인겁니다.


  1. int num1 = 123;  
  2. int *obj = new int;  
  3. (*obj) = num1;  

이 때문에 MSDN에서도 Boxing, Unboxing에 대해 아래와 같이 조언하고 있습니다.

  • System.Collections.ArrayList 같은 제네릭이 아닌 컬렉션 클래스의 예와 같이 많은 수의 boxing이 필요한 경우에는 값 형식을 사용하지 않는 것이 좋습니다. System.Collections.Generic.List 같은 제네릭 컬렉션을 사용하면 값 형식의 boxing을 방지할 수 있습니다. boxing 및 unboxing 과정에는 많은 처리 작업이 필요합니다. 값 형식을 boxing할 때는 완전히 새로운 개체가 만들어져야 하며, 이러한 작업은 간단한 참조 할당보다 최대 20배의 시간이 걸립니다. unboxing을 할 때는 캐스팅 과정에 할당 작업보다 4배의 시간이 걸릴 수 있습니다.


반대로 Unboxing의 경우 Boxing과는 다르게 대입하려는 타입을 명시적으로 지정해주어야 합니다. Bxoing 되어있는 타입과 명시한 타입이 같으면, 해당 객체의 값을 복사합니다.



C#은 C/C++에 비해 타입에 엄격하기 때문에 Boxing 타입과 다른 타입으로 Unboxing을 시도하면 InvalidCastException 같은 에러를 냅니다.


참조 : MSDN Boxing 및 Unboxing


반응형
반응형

https://msdn.microsoft.com/ko-kr/library/67ef8sbd.aspx


C# 프로그래밍 가이드

이 단원에서는 C# 언어의 주요 기능 및 C#에서 .NET Framework를 통해 액세스할 수 있는 기능에 대한 자세한 정보를 제공합니다.

이 단원에서는 독자가 C# 및 일반적인 프로그래밍 개념에 대해 잘 알고 있다고 가정합니다. 프로그래밍이나 C#에 처음 입문하는 사용자는 C# 개발자 센터에서 처음 시작할 때 도움이 되는 많은 자습서, 샘플 및 비디오를 찾을 수 있습니다.

특정 키워드, 연산자 및 전처리기 지시문에 대한 자세한 내용은 C# 참조를 참조하십시오. C# 언어 사양에 대한 자세한 내용은 C# 언어 사양을 참조하십시오.


반응형
반응형

요즘 길가다 짬날때 보고 있는 C# .. 잼있네 ㅋㅋㅋ

http://www.csharp-station.com/ 여기 설명잘되있는사이트 추천! ㅋㅋ




Welcome

Welcome to C# Station!  This is a community site for people interested in applying .NET using the C# programming language.  We've been around since July 4th 2000 and have continued to grow over the years.  Items of interest include Articles, Books, Links, Documentation,  and Tutorials. More...

Who Operates this Site?

This site is owned and operated by Joe Mayo. Besides articles and tutorials on this site, Joe is a published author. His latest book, LINQ Programming is available now. Joe has a blog too. You can also follow Joe on Twitter.

Also check out What’s New in Visual C# 4.0? by Joe Mayo.

Source Code

If you would like to see an entire application written in C#, visit LINQ to Twitter, an open source LINQ Provider for the Twitter Micro-Blogging Service.

What is C#?

C# (pronounced "see sharp" or "C Sharp") is one of many .NET programming languages. It is object-oriented and allows you to build reusable components for a wide variety of application types Microsoft introduced C# on June 26th, 2000 and it became a v1.0 product on Feb 13th 2002.

C# is an evolution of the C and C++ family of languages. However, it borrows features from other programming languages, such as Delphi and Java. If you look at the most basic syntax of both C# and Java, the code looks very similar, but then again, the code looks a lot like C++ too, which is intentional. Developers often ask questions about why C# supports certain features or works in a certain way. The answer is often rooted in it's C++ heritage.

How Does a C# Application Run?

An important point is that C# is a "managed" language, meaning that it requires the .NET Common Language Runtime (CLR) to execute. Essentially, as an application that is written in C# executes, the CLR is managing memory, performing garbage collection, handling exceptions, and providing many more services that you, as a developer, don't have to write code for. The C# compiler produces Intermediate Language (IL) , rather than machine language, and the CLR understands IL. When the CLR sees the IL, it Just-In-Time (JIT) compiles it, method by method, into compiled machine code in memory and executes it. As mentiond previously, the CLR manages the code as it executes.

Because C# requires the CLR, you must have the CLR installed on your system. All new Windows operating systems ship with a version of the CLR and it is available via Windows Update for older systems. The CLR is part of the .NET, so if you see updates for the .NET Framework Runtime, it contains the CLR and .NET Framework Class Library (FCL). It follows that if you copy your C# application to another machine, then that machine must have the CLR installed too.

Does C# Have a Runtime Library?

Instead of a runtime library (such as APIs for file I/O, string handling, etc.) being dedicated to a single language, .NET ships with a .NET Framework Class Library (FCL), which includes literally tens of thousands of reusable objects. Since all .NET languages target the CLR with the same IL, all languages can use the FCL. This shortens the learning curve for any developer moving from one .NET language to another, but also means that Microsoft is able to add many more fea,tures because there is only one FCL, rather than a separate implementation for common features in every programming language. Similarly, 3rd party software vendors can write managed code that any .NET developer, regardless of language, can use. In addition to all of the services you would expect of a runtime library, such as collections, file I/O, networking, etc., the FCL includes the APIs for all of the other .NET technologies, such as for desktop and Web development.

What can I do with C#?

C# is only a programming language. However, because C# targets the CLR and has access to the entire FCL, there's a lot you can do. To get an idea of the possibilities, open the FCL and look at the available technologies. You can write desktop applications with Windows Forms,Windows Presentation Foundation (WPF), or even Console applications. For the Web, you can write ASP.NET and Silverlight applications in addition to enabling systems to communicate with Web Services with Windows Communications Foundation (WCF). When you need to access data, there is both ADO.NET and LINQ. Some of Microsoft's newest technologies include Windows 8Windows Phone 8, and Windows Windows Azure. Of course, these are only a few of the technologies available and as a general purpose programming language, you can do a lot more than this with C#.

How Do I Get Started?

By visiting this page and reading this far, you've already begun.  You can continue your journey with the Free C# Tutorial right here at C# Station. The C# Tutorial was created to help beginning developers and other professionals who need a quick on-ramp to the language.


반응형
반응형

http://www.microsoft.com/Korea/MSDN/MSDNMAG/ISSUES/2001/ctocsharp/default.aspx


C++ -> C#: C++에서 C#으로의 전환을 위해 알아 두어야 할 사항

Jesse Liberty
이 글을 이해하려면 C++에 대한 기본 지식이 있어야 합니다.
난이도? ?? 1?? 2?? 3?
요약 :C#는 C++의 구문과 의미를 바탕으로 설계되었으므로, C 프로그래머는 .NET 및 공용 언어 런타임을 충분히 활용할 수 있습니다. C++에서 C#으로의 전환은 비교적 쉬운 작업이지만, new, struct, 생성자 및 소멸자에 대한 변경 사항을 포함하여 몇 가지 유의해야 할 부분도 있습니다. 이 글에서는 가비지 수집, 속성, foreach 루프 및 인터페이스와 같은 C#에서 새로운 언어 기능을 살펴봅니다. 인터페이스를 다루면서 속성, 배열 및 기본 클래스 라이브러리도 함께 다룰 것입니다. 이어서, 비동기 I/O, 특성 및 검토, 형식 발견, 동적 호출 등에 대해 살펴봅니다.
거의 10년을 주기로 개발자들은 새로운 프로그래밍 기술을 익히기 위해 시간과 노력을 투자하고 있습니다. 1980년대 초에는 Unix와 C, 1990년대 초에는 Windows와 C++, 그리고 지금은 Microsoft .NET Framework와 C#이 대표적입니다. 이러한 과정이 진행되면서 들인 비용보다는 얻는 이익이 훨씬 더 많았습니다. 반가운 소식은 C#과 .NET으로 이루어지는 거의 모든 프로젝트의 분석 및 디자인 단계가 C++과 Windows의 방식에서도 바뀐 점이 별로 없다는 것입니다. 하지만 새로운 환경에서의 프로그래밍 접근 방식에는 상당한 차이가 있습니다. 이 글에서는 C++ 프로그래밍에서 C# 프로그래밍으로 전환할 수 있는 방법과 정보를 소개합니다.
 C# 구현의 향상 내용에 대해 소개한 자료는 많으므로 여기에서 다시 반복하지는 않겠습니다. 대신, C++과 C# 사이에 가장 많이 변경되었다고 생각되는 사항, 즉 관리가 없는 환경에서 관리 가능한 환경으로의 변화에 대해 집중적으로 살펴볼 것입니다. 또한 부주의한 C++ 프로그래머들이 주의해야 할 몇 가지 중요한 함정에 대해 소개하고, 프로그램 구현 방법에 영향을 미치는 새로운 언어 기능에 대해서도 살펴볼 것입니다.

맨 위로


관리 가능한 환경으로 이동

 C++은 플랫폼에 구애 받지 않는 하위 단계 수준 의 개체 지향형 프로그래밍 언어로 고안되었습니다. C#은 C++보다 상위 단계 수준 의 구성 요소 지향형 언어로 고안되었습니다. 관리 가능한 환경으로의 이동은 프로그래밍이라는 영역에서는 커다란 변화입니다. C#은 정확한 제어 보다는 전체적인 모습을 볼 수 있는 프레임워크를 제공합니다.
 예를 들어, C++의 경우 생성은 물론 개체의 레이아웃에 대해서도 많은 제어 권한을 가집니다. 즉 C++은 배치 연산자 new를 사용하여 개체를 다른 개체 스택 및 힙 위에 또는 메모리의 특정 위치에도 만들 수 있습니다.
 .NET의 관리 가능한 환경에서는 이러한 수준의 제어를 포기해야 합니다. 개체의 형식을 선택하면 해당 개체가 어디에 만들어지는지 암시적으로 정해집니다. 일부 형식(int, double 및 long)은 항상 스택(다른 개체 내부에 포함되는 경우 제외)에 만들어지고, 클래스는 항상 힙에 만들어집니다. 개체를 힙의 어디에 만들 것인지 제어할 수 없고, 해당 주소를 얻을 수 없으며, 특정 메모리 위치에 둘 수도 없습니다. 이러한 제약 사항을 해결할 수 있는 방법이 있기는 하지만 이 글의 주제에서 벗어나므로 생략하도록 합니다.
 이제는 더 이상 개체의 수명을 제어할 수 없습니다. C#에는 소멸자가 없습니다. 개체 저장 영역에 대한 참조가 더 이상 없을 경우에는 가비지 수집기가 해당 항목의 저장 영역을 회수할 것이지만, 그 시기에 대해서는 알 수 없습니다.
 C#의 구조를 통해 기본 프레임워크를 알 수 있습니다. 다중 상속은 관리되거나 가비지 수집이 이루어지는 환경에서는 효과적으로 구현하기가 매우 어렵고, 일반 사항은 프레임워크에서 구현되지 않았기 때문에 다중 상속과 템플릿이 없습니다.
 간단한 C# 형식은 하부 CLR(공용 언어 런타임) 형식에 대한 매핑에 불과합니다. 예를 들면, C# int는 System.Int32에 매핑됩니다. C#에서 형식은 언어가 아닌 공용 형식 시스템에 의해 결정됩니다. 사실, 사용자가 Visual Basic 개체에서 C# 개체를 만들어 내는 능력을 보유하려면 모든 .NET 언어에 의해 공유되는 기능인 공용 언어 하위 집합에 종속되어야 합니다.
 반면, 관리 가능한 환경 및 CLR를 통해 많은 이점을 얻을 수 있습니다. 가비지 수집 및 모든 .NET 언어에서의 단일 형식 시스템 이외에도, 크게 향상된 구성 요소 기반 언어를 얻게 됩니다. 구성 요소 기반 언어는 버전 관리를 완벽하게 지원하고 리플렉션를 통해 런타임에서 사용 가능한 확장가능 메타 데이터를 제공합니다. 후기 바인딩을 특별히 지원할 필요도 없습니다. 형식 찾기 및 후기 바인딩은 언어에 포함되어 있습니다. C#에서는 열거 및 속성이 언어의 첫 번째 클래스이고 이벤트 및 대리자(형식에 관계 없는 함수 포인터)와 마찬가지로 하부 엔진에 의해 완벽하게 지원됩니다.
 하지만 관리 가능한 환경의 최대 장점은 .NET Framework입니다. 프레임워크는 모든 .NET 언어에서 사용할 수 있고, C#은 프레임워크의 풍부한 클래스, 인터페이스 및 개체로 프로그래밍할 수 있도록 최적으로 고안된 언어입니다.

맨 위로


함정

 C#은 C++와 크게 달라 보이지 않기 때문에 전환이 쉬울 수 있지만, 여기에는 분명히 유의해야 할 함정이 있습니다. C++에서는 완벽하게 보이는 코드를 작성한 후 컴파일되지 않거나, 심한 경우 예상대로 작동하지 않을 수도 있습니다. C++에서 C#으로의 구문상 변화는 크지 않습니다. 다만 클래스 선언 뒤에 세미콜론이 없고, Main을 대문자로 시작한다는 정도입니다. 구문상의 변화를 쉽게 참고할 수 있도록 이러한 목록을 게시한 웹 페이지를 만들고 있지만, 컴파일러에서 쉽게 다룰 수 있으므로 여기에서는 다루지 않겠습니다. 그렇다고 해도 문제를 일으킬 수 있는 몇 가지 중요한 변화는 있음을 강조하고 싶습니다.

맨 위로


참조 및 값 형식
 C#에서는 값 형식과 참조 형식을 구분합니다. 단순 형식(int, long, double 등) 및 struct는 값 형식이고, 모든 클래스는 개체와 마찬가지로 참조 형식입니다. 값이 참조 형식 내부에 포함되지 않을 경우, 값 형식은 C++의 변수와 같이 스택에서 값을 가집니다. 참조 형식 변수는 스택에 있지만, C++의 포인터와 유사하게 힙에서 개체의 주소를 가집니다. 값 형식은 값(복사본)에 의해 메서드에 전달되는 반면, 참조 형식은 참조에 의해 효율적으로 전달됩니다.

맨 위로


Struct
 Struct는 C#에서 많이 다릅니다. C++의 경우 struct는 기본 상속 및 기본 액세스가 전용이 아닌 공용이라는 점만 제외하면 클래스와 동일합니다. 그러나 C#에서의 struct는 클래스와 매우 다릅니다. C#의 struct는 경량 개체를 캡슐화하기 위해 디자인되었습니다. 즉 struct는 참조 형식이 아닌 값 형식이므로 값에 의해 전달됩니다. 또한 클래스에 적용되지 않는 제한 사항이 있습니다. 예를 들어, struct는 봉인되어 있습니다, 즉 struct는 개체에서 파생되는 System.ValueType에서 파생될 수 없으며? 그것과 다른 기본 클래스를 가질 수도 없습니다. struct는 기본(parameterless) 생성자를 선언할 수 없습니다.
 반면, struct는 클래스보다 더욱 효율적이므로 경량 개체 생성에 가장 적합합니다. struct의 봉인 및 값 의미가 문제가 안 된다면 아주 작은 개체를 만들 때는 클래스보다 struct를 사용하는 것이 훨씬 유리합니다.

맨 위로


모든 것은 개체에서 파생
 C#의 경우 모든 것은 결국 개체에서 파생됩니다. 여기에는 생성 클래스는 물론 int 또는 struct와 같은 값 형식이 포함됩니다. 개체 클래스는 ToString과 같은 유용한 메서드를 제공합니다. 예를 들면, C#의 cout라 할 수 있는 System.Console.WriteLine 메서드인 ToString을 사용할 경우입니다. 이 메서드는 오버로드되어 개체의 문자열 및 배열을 가집니다.
 WriteLine을 사용하려면 기존 방식의 printf와는 달리 대체 매개 변수를 제공해야 합니다. myEmployee가 사용자 지정된 Employee 클래스의 인스턴스이고 myCounter가 사용자 지정된 Counter 클래스의 인스턴스라고 가정해 보겠습니다. 다음과 같은 코드를 작성할 수 있습니다.
Console.WriteLine("The employee: {0}, the counter value: {1}", myEmployee, myCounter);
WriteLine은 각 개체에 대해 가상 메서드인 Object.ToString을 호출하여 매개 변수 대신 반환된 문자열로 대체합니다. Employee 클래스가 ToString을 재정의하지 않을 경우, 기본 구현(System.Object에서 파생)이 호출되어 문자열로 클래스의 이름을 반환합니다. Counter는 ToString을 재정의하여 정수 값을 반환할 것입니다. 이 경우, 출력은 다음과 같습니다.
The employee: Employee, the counter value: 12
 정수 값을 WriteLine에 전달할 경우는 어떻게 될까요? 정수에 대해 ToString을 호출할 수는 없지만, 컴파일러는 값이 정수 값으로 설정될 개체의 인스턴스에서 int를 암시적으로 가둘 수 있습니다. WriteLine이 ToString을 호출할 경우 개체는 정수의 값을 나타내는 문자열을 반환합니다. (그림?1 참고)

맨 위로


Reference 매개 변수 및 Out 매개 변수
 C++과 마찬가지로 C#에서도 메서드는 하나의 반환 값만 가질 수 있습니다. C++의 경우 포인터 또는 참조를 매개 변수로 전달하여 이 제한 사항을 해결할 수 있었습니다. 호출된 메서드가 매개 변수를 변경하고, 호출하는 메서드는 다시 새로운 값을 사용할 수 있습니다.
 참조를 메서드에 전달하면 C++에서 참조 또는 포인터 전달을 통해 액세스하는 방식과 동일하게 원래 개체에 액세스할 수 있습니다. 하지만 이런 방식이 값 형식의 경우에는 해당되지 않습니다. 값 형식을 참조로 전달하려는 경우에는 값 형식 매개 변수를 ref 키워드로 표시해야 합니다.
public void GetStats(ref int age, ref int ID, ref int yearsServed)
ref 키워드는 메서드 선언과 메서드에 대한 실제 호출 모두의 경우에 사용해야 한다는 점을 유의하십시오.
Fred.GetStats(ref age, ref ID, ref yearsServed);
이제 호출 메서드에서 age, ID 및 yearsServed를 선언하고 GetStats로 전달하면 변경된 값을 얻게 됩니다.
 C#에서는 명확한 할당이 필요합니다. 즉 로컬 변수, age, ID 및 yearsServed는 GetStats를 호출하기 전에 초기화되어야 합니다. 이것은 불필요한 수고입니다. 이것들은 단지 GetStats에서 값을 얻기 위해서만 사용되기 때문입니다. 이 문제를 해결하기 위해 C#에서는 out 키워드도 제공합니다. 이 키워드는 초기화되지 않은 변수를 전달하고 참조에 의해 전달되도록 지정합니다. 다음은 이러한 의도를 명시적으로 나타내는 방법입니다.
public void GetStats(out int age, out int ID, out int yearsServed)
이 때, 호출 메서드는 일치해야 합니다.
Fred.GetStats(out age,out ID, out yearsServed);

맨 위로


New 호출
 C++의 경우, 키워드 new는 힙의 개체를 인스턴스화합니다. 하지만 C#의 경우는 그렇지 않습니다. 참조 형식의 경우, new 키워드는 힙의 개체를 인스턴스화합니다. 하지만 struct와 같은 값 형식의 경우 개체는 스택에서 생성되고 생성자가 호출됩니다.
 사실, new를 사용하지 않고 스택에 struct를 생성할 수도 있지만 조심해야 합니다. new는 개체를 초기화합니다. new를 사용하지 않을 경우 struct의 값을 사용하기 전에, 즉 메서드에 전달하기 전에 모든 값을 직접 초기화해야 하며, 그렇지 않을 경우 컴파일되지 않습니다. 또한 명확한 할당을 위해서는 모든 개체가 초기화되어야 합니다. (그림?2 참고)

맨 위로


속성
 대부분의 C++ 프로그래머는 멤버 변수를 전용으로 유지하려고 합니다. 이러한 데이터 숨김을 통해 캡슐화가 용이하고 클라이언트가 의존하는 인터페이스를 건드리지 않고도 클래스의 구현을 변경할 수 있기 때문입니다.그러나 클라이언트가 직접 이러한 멤버의 값을 얻거나 설정할 수 있게 하기 위하여, C++ 프로그래머는 전용 멤버 변수의 값을 수정하는 일을 담당하는 접근자 메서드를 생성합니다.
 C#에서 속성은 클래스의 첫 번째 클래스 멤버입니다. 클라이언트에게 속성은 멤버 변수로 보이지만, 클래스 구현자에게는 메서드로 보입니다. 이런 개념은 적절하다고 볼 수 있습니다. 즉 프로그래머는 완전한 캡슐화 및 데이터 숨김이 가능하고 클라이언트는 멤버에 쉽게 액세스할 수 있기 때문입니다.
 Employee 클래스에 Age 속성을 제공하여 클라이언트가 employee의 age 멤버를 얻고 설정하도록 할 수 있습니다.
public int Age { get { return age; } set { age = value; } }
 키워드 값은 속성에서 암시적으로 사용할 수 있습니다. 다음과 같이 작성하는 경우,
Fred.Age = 17;
컴파일러는 값으로 17을 전달합니다.
 Set 접근자가 아닌 Get 접근자를 사용하여 YearsServed에 대해 읽기 전용 속성을 만들 수도 있습니다.
public int YearsServed { get { return yearsServed; } }
이러한 접근자를 사용하기 위해 드라이버 프로그램을 변경할 경우 어떻게 작동하는지 볼 수 있습니다. (그림?3 참고)
 속성을 통해 Fred의 age를 얻은 다음, 이 속성을 사용하여 age를 설정할 수 있습니다. YearsServed 속성을 액세스하여 값을 얻을 수 있지만 설정할 수는 없습니다. 마지막 줄에서 주석을 제거하지 않을 경우 프로그램이 컴파일되지 않습니다.
 나중에 데이터베이스에서 Employee의 age를 가져오려는 경우 접근자 구현만 변경하면 됩니다. 클라이언트는 아무런 영향을 받지 않습니다.

맨 위로


배열
 C#에서는 기존의 C/C++ 배열보다 개선된 버전의 배열 클래스를 제공합니다. 예를 들어, C# 배열의 범위를 벗어나서 작성하는 것은 불가능합니다. 또한 Array에는 더 진보적인 기능의 ArrayList도 있습니다. ArrayList는 프로그램의 크기 변화에 대한 요구 사항을 동적으로 관리할 수 있습니다.
 C#에서 배열은 1차원, 다차원 사각형 배열(C++ 다차원 배열과 유사) 및 가변 배열(배열안의 배열)의 세 종류로 사용 가능합니다.
 1차원 배열은 다음과 같이 만들 수 있습니다.
int[] myIntArray = new int[5];
또는, 다음과 같이 초기화할 수도 있습니다.
int[] myIntArray = { 2, 4, 6, 8, 10 };
 다차원 사각형 배열은 다음과 같이 만들 수 있습니다.
int[,] myRectangularArray = new int[rows, columns];
또는, 다음과 같이 간단히 초기화할 수도 있습니다.
int[,] myRectangularArray = { {0,1,2}, {3,4,5}, {6,7,8}, {9,10,11} };
 가변 배열은 배열안의 배열이므로 1차원만 제공하면 됩니다.
int[][] myJaggedArray = new int[4][];
그런 다음, 다음과 같이 각 내부 배열을 만듭니다.
myJaggedArray[0] = new int[5]; myJaggedArray[1] = new int[2]; myJaggedArray[2] = new int[3]; myJaggedArray[3] = new int[5];
 배열은 System.Array 개체에서 파생되므로 Sort 및 Reverse를 포함하여 많은 수의 유용한 메서드와 함께 사용됩니다.

맨 위로


인덱서
 자신만의 배열 같은 개체를 만들 수도 있습니다. 예를 들면, 표시가 가능한 문자열 집합을 가진 목록 상자를 만들 수 있습니다. 배열에서 하듯이 인덱스로 상자의 내용을 액세스할 수 있다면 편리할 것입니다.
string theFirstString = myListBox[0]; string theLastString = myListBox[Length-1];
이것은 인덱서를 이용하면 가능합니다. 인덱서는 속성과 비슷한 점이 많지만, 인덱스 연산자의 구문을 지원합니다. 그림 4에서는 속성을 보여주는데 속성의 이름 다음에 인덱스 연산자가 옵니다.
 그림?5는 아주 간단한 ListBox 클래스를 구현하고 인덱스 기능을 제공하는 방법을 보여 줍니다.


맨 위로


인터페이스
 소프트웨어 인터페이스는 두 형식이 상호 작용하는 방식에 대한 계약입니다. 한 형식이 인터페이스를 등록하면 클라이언트에게 "다음 메서드, 속성, 이벤트 및 인덱서에 대한 지원을 약속한다"는 이야기를 하는 것입니다. 
 C#은 개체 지향형 언어이므로, 이러한 계약은 인터페이스라는 엔티티로 캡슐화됩니다. 인터페이스 키워드는 계약을 캡슐화하는 참조 형식을 선언합니다.
 개념적으로 보면, 인터페이스는 추상 클래스와 유사합니다. 차이가 있다면, 추상 클래스는 파생된 클래스의 패밀리에 대한 기본 클래스로 제공되고, 인터페이스는 다른 상속 트리와 혼합됩니다.


맨 위로


IEnumerable 인터페이스
 이전 예제로 다시 돌아가서, 일반 배열로 할 수 있는 것과 마찬가지로 foreach 루프를 사용하여 ListBoxTest 클래스에서 문자열을 출력할 수 있다면 좋을 것입니다. 이것은 클래스에서 IEnumerable 인터페이스를 구현해서 할 수 있고, IEnumerable 인터페이스는 다시 foreach 생성에 의해 암시적으로 사용됩니다. IEnumerable는 열거 및 foreach 루프를 지원하는 모든 클래스에서 구현됩니다.
 IEnumerable은 단 하나의 메서드인 GetEnumerator를 가지는데, 이 메서드의 임무는 IEnumerator의 특수한 구현을 반환하는 것입니다. 따라서, Enumerable 클래스의 의미를 통해 Enumerator를 제공할 수 있습니다.
 Enumerator는 IEnumerator 메서드를 구현해야 합니다. 이것은 컨테이너 클래스 또는 별도의 클래스에 의해 직접 구현할 수 있습니다. 이 중에서 두 번째 방식이 일반적으로 선호되는데, 그 이유는 컨테이너를 흩어 놓는 대신 Enumerator 클래스에서 해야 할 일을 캡슐화하기 때문입니다.
 이미 그림 5에서 보았던 ListBoxTest에 Enumerator를 추가할 것입니다. Enumerator 클래스는 컨테이너 클래스에만 해당되기 때문에(즉 ListBoxEnumerator는 ListBoxTest에 대해 많이 알고 있어야 하므로) ListBoxTest 내에 포함시켜 전용 구현으로 만들 것입니다.
 이러한 의미에서 ListBoxTest는 IEnumerable 인터페이스 구현을 위한 것으로 정의할 수 있습니다. IEnumerable 인터페이스는 Enumerator를 반환해야 합니다.
public IEnumerator GetEnumerator() { return (IEnumerator) new ListBoxEnumerator(this); }
메서드가 현재 ListBoxTest 개체(this)를 열거자에게 전달한다는 점을 알아두십시오. 이를 통해, 열거자가 이 특정 ListBoxTest 개체를 열거할 수 있게 됩니다.
 여기에서 Enumerator를 구현하기 위한 클래스는 ListBoxTest 내에서 정의된 전용 클래스인 ListBoxEnumerator로 구현되었습니다. 이 클래스의 임무는 매우 분명합니다.
 열거되는 ListBoxTest는 인수로 생상자에게 전달되고, 여기에서 멤버 변수인 myLBT에게 할당됩니다. 또한 생성자는 멤버 변수 인덱스를 -1로 설정하여 개체 나열이 아직 시작되지 않았음을 나타냅니다.
public ListBoxEnumerator(ListBoxTest theLB) { myLBT = theLB; index = -1; }
 MoveNext 메서드가 인덱스를 증가시킨 다음 열거하려는 개체의 마지막 부분을 넘어서 실행하지 않도록 확인합니다. 그럴 경우 false가 반환되고, 그렇지 않을 경우 true가 반환됩니다.
public bool MoveNext() { index++; if (index >= myLBT.myStrings.Length) return false; else return true; }
Reset은 인덱스를 -1로 재설정합니다.
 Current 속성은 마지막으로 추가된 문자열을 반환하기 위해 구현됩니다. 이것은 임의의 결정입니다. 다른 클래스에서 Current는 디자이너가 결정한 모든 해당 의미를 가지게 됩니다. 하지만 정의될 경우에는 현재 멤버에 대한 액세스는 열거자가 수행하는 임무이므로 모든 열거자가 현재 멤버를 반환할 수 있어야 합니다.
public object Current { get { return(myLBT[index]); } }
 이제 다 되었습니다. foreach에 대한 호출이 열거자를 페칭하고 이것을 사용하여 배열에 걸쳐 열거합니다. foreach는 의미 있는 값을 추가했는지 여부에 상관 없이 모든 문자열을 표시하므로, 관리하기 쉽게 표시 위해 myStrings의 초기화를 여덟 개의 항목으로 변경하였습니다.
myStrings = new String[8];

맨 위로


기본 클래스 라이브러리 사용
 C#이 C++과 어떻게 다르고, 문제 해결을 위한 접근 방식이 어떻게 바뀌어야 하는지에 대한 이해를 돕기 위해 다소 간단한 예제를 살펴보도록 하겠습니다. 큰 텍스트 파일을 읽고 화면에 해당 내용을 표시하는 클래스를 만들 것입니다. 이것을 다중 스레드 프로그램으로 만들어 데이터를 디스크에서 읽는 동안 다른 작업을 할 수 있도록 할 것입니다.
 C++에서는 파일을 읽는 스레드를 만들고, 다른 작업을 수행하는 스레드를 별도로 만들어야 합니다. 이러한 스레드는 독립적으로 작동하지만 동기화가 필요합니다. 이러한 모든 것을 C#에서도 할 수 있지만, 대부분의 경우 자신의 스레드를 작성할 필요는 없습니다. .NET에서 비동기 I/O를 위한 매우 강력한 메커니즘을 제공하기 때문입니다.
 비동기 I/O 지원은 CLR에 포함되어 있고 일반적인 I/O 스트림 클래스와 같이 사용이 쉽습니다. 컴파일러에게 많은 수의 System 네임스페이스에서 개체를 사용할 것임을 알리는 것부터 시작합니다.
using System; using System.IO; using System.Text;
System을 포함시켜도 그 밑의 모든 하위 네임스페이스가 자동으로 포함되지 않기 때문에 using 키워드를 사용하여 명시적으로 포함시켜야 합니다. I/O 스트림 클래스를 사용할 것이므로 System.IO가 필요하고, 바이트 스트림을 ASCII로 인코딩하기 위해서는 System.Text도 필요 합니다.
 이 프로그램 작성 단계는 매우 간단합니다. .NET이 대부분의 작업을 대신해 주기 때문입니다. Stream 클래스의 BeginRead 메서드를 사용할 것입니다. 이 메서드는 비동기 I/O를 제공하고, 버퍼에 가득 찬 데이터를 읽은 다음, 버퍼 프로세싱 준비가 되면 콜백 메서드를 호출합니다.
 콜백 메서드에 대한 버퍼 및 대리자로 바이트 배열을 전달해야 합니다. 이 두 가지를 드라이버 클래스의 전용 멤버 변수로 선언합니다.
public class AsynchIOTester { private Stream inputStream; private byte[] buffer; private AsyncCallback myCallBack;
멤버 변수 inputStream은 Stream 형식에 속하고 BeginRead 메서드를 호출하는 개체에 있으며, 대리자(myCallBack) 및 버퍼를 전달됩니다. 대리자는 멤버 함수에 대한 형식에 관계 없는 포인터와 매우 유사합니다. C#에서 대리자는 언어의 첫 번째 클래스 요소입니다.
 .NET은 바이트가 디스크의 파일에서 채워져 데이터를 처리할 수 있을 때 대리된 메서드를 호출합니다. 기다리는 동안 다른 작업을 수행할 수 있습니다. (이 경우 정수를 1에서 50,000으로 올릴 수 있지만, 생산 프로그램에서는 사용자와 상호 작용하거나 다른 유용한 작업을 수행할 수 있습니다.)
 .NET은 바이트가 디스크의 파일에서 채워져 데이터를 처리할 수 있을 때 대리된 메서드를 호출합니다. 기다리는 동안 다른 작업을 수행할 수 있습니다. (이 경우 정수를 1에서 50,000으로 올릴 수 있지만, 생산 프로그램에서는 사용자와 상호 작용하거나 다른 유용한 작업을 수행할 수 있습니다.)
public delegate void AsyncCallback (IAsyncResult ar);
 따라서, 이 대리자는 void를 반환하는 다른 모든 메서드와 연결될 수 있고 매개 변수로 IAsyncResult 인터페이스를 가집니다. CLR은 메서드가 호출되면 런타임에 IAsyncResult 인터페이스를 전달하므로, 다음과 같이 메서드만 선언하면 됩니다.
void OnCompletedRead(IAsyncResult asyncResult)
그런 다음, 생성자에서 대리자를 연결합니다.
AsynchIOTester() { ??? myCallBack = new AsyncCallback(this.OnCompletedRead); }
이것은 멤버 변수 myCallback(이전에 형식 AsyncCallback에 속하도록 정의)에게 대리자 인스턴스를 할당합니다. 대리자 인스턴스는 AsyncCallback 생성자를 호출하고 대리자와 연결하려는 메서드로 전달함 으로써 만들어집니다.
 다음은 전체 프로그램이 단계별로 작동하는 방식입니다. Main에서 클래스의 인스턴스를 만들고 실행되도록 합니다.
public static void Main() { AsynchIOTester theApp = new AsynchIOTester(); theApp.Run(); }
new에 대한 호출로 생성자가 실행됩니다. 생성자에서 파일을 열고 Stream 개체를 다시 얻습니다. 그런 다음, 버퍼에 공간을 할당하고 콜백 메커니즘을 연결합니다.
AsynchIOTester() { inputStream = File.OpenRead(@"C:\MSDN\fromCppToCS.txt"); buffer = new byte[BUFFER_SIZE]; myCallBack = new AsyncCallback(this.OnCompletedRead); }
Run 메서드에서 BeginRead를 호출하면 파일의 비동기적 읽기가 이루어집니다.
inputStream.BeginRead( buffer, // where to put the results 0, // offset buffer.Length, // how many bytes (BUFFER_SIZE) myCallBack, // call back delegate null); // local state object
이제 다른 작업으로 이동합니다.
for (long i = 0; i < 50000; i++) { if (i%1000 == 0) { Console.WriteLine("i: {0}", i); } }
읽기가 완료되면 CLR이 콜백 메서드를 호출합니다.
void OnCompletedRead(IAsyncResult asyncResult) {
OnCompletedRead에서 첫 번째로 해야 할 일은 Stream 개체의 EndRead 메서드를 호출하여 얼마나 많은 바이트가 읽혀졌는지 알아내고, 공용 언어 런타임에 의해 전달된 IAsyncResult 인터페이스 개체를 전달 하는 것입니다.
int bytesRead = inputStream.EndRead(asyncResult);
 EndRead 호출의 결과는 읽은 바이트의 수를 다시 받는 것이고. 수가 0보다 클 경우 버퍼를 문자열로 변환하여 콘솔에 쓴 다음, 다른 비동기 읽기를 유발하기위해 BeginRead를 다시 호출합니다.
if (bytesRead > 0) { String s = Encoding.ASCII.GetString(buffer, 0, bytesRead); Console.WriteLine(s); inputStream.BeginRead(buffer, 0, buffer.Length, myCallBack, null); }
이제 읽기가 진행되는 동안 다른 작업(이 경우는 50,000까지 카운트)을 수행할 수도 있지만, 버퍼가 가득 찰 때마다 읽은 데이터를 처리(이 경우는 콘솔에 출력)할 수 있습니다. 이 예제에 대한 전체 소스 코드 AsynchIO.cs는 이 글의 맨 위쪽에 있는 링크에서 다운로드할 수 있습니다.
 비동기 I/O의 관리는 전적으로 CLR에서 제공합니다. 네트워크에 대한 다음 내용을 읽어보면 이에 대한 장점을 더 많이 알게 될 것입니다.

맨 위로


네트워크를 통한 파일 읽기
 C++에서 네트워크를 통한 파일 읽기는 쉽지 않은 프로그래밍 작업입니다. .NET에서 이점을 폭넓게 지원합니다. 사실, 네트워크를 통한 파일 읽기는 표준 Base Class Library Stream 클래스 사용에 불과합니다.
 먼저, TCPListener 클래스의 인스턴스를 만들어 TCP/IP 포트(이 경우는 포트 65000)에서 수신하도록 합니다.
TCPListener tcpListener = new TCPListener(65000);
생성되었으면 TCPListener 개체가 수신을 시작하도록 합니다.
tcpListener.Start();
이제 클라이언트의 연결 요청을 기다립니다.
Socket socketForClient = tcpListener.Accept();
TCPListener 개체의 Accept 메서드는 Socket 개체를 반환하고, 이 개체는 표준 Berkeley 소켓 인터페이스를 나타내며 특정한 끝 지점(이 경우는 클라이언트)에 바인딩됩니다. Accept는 동기 메서드이고 연결 요청을 받을 때까지는 반환되지 않습니다. 소켓이 연결되면 클라이언트에게 파일을 보낼 준비가 마무리됩니다.
if (socketForClient.Connected) { ???
그 다음, NetworkStream 클래스를 만들어 소켓을 생성자에게 전달합니다.
NetworkStream networkStream = new NetworkStream(socketForClient);
그런 다음, 이전과 같이 StreamWriter 개체를 만들기는 하지만, 이번에는 파일이 아닌 방금 생성된 NetworkStream에서 만듭니다.
System.IO.StreamWriter streamWriter = new System.IO.StreamWriter(networkStream);
이 스트림에 쓰면 스트림이 네트워크를 통해 클라이언트에게 전송됩니다. 전체 소스 코드 TCPServer.cs도 다운로드가 가능합니다.

맨 위로


클라이언트 생성
 클라이언트는 호스트에 TCP/IP 클라이언트 연결을 나타내는 TCPClient 클래스를 인스턴스화합니다.
TCPClient socketForServer; socketForServer = new TCPClient("localHost", 65000);
이 TCPClient로 NetworkStream을 만들고, 이 스트림에 대해 StreamReader를 만들 수 있습니다.
NetworkStream networkStream = socketForServer.GetStream(); System.IO.StreamReader streamReader = new System.IO.StreamReader(networkStream);
이제 스트림에 데이터가 있는 동안 계속 스트림을 읽고 결과를 콘솔에 출력합니다.
do { outputString = streamReader.ReadLine(); if( outputString != null ) { Console.WriteLine(outputString); } } while( outputString != null );
이것을 테스트하기 위해 간단한 테스트 파일을 만듭니다.
This is line one This is line two This is line three This is line four
다음은 서버에서의 출력입니다.
Output (Server) Client connected Sending This is line one Sending This is line two Sending This is line three Sending This is line four Disconnecting from client... Exiting...
다음은 클라이언트에서의 출력입니다.
This is line one This is line two This is line three This is line four

맨 위로


속성 및 메타데이터
 C#과 C++ 사이의 큰 차이점 중 하나는 C#에서는 메타데이터, 즉 클래스, 개체, 메서드 등에 대한 데이터를 기본적으로 지원한다는 것입니다. 특성은 CLR의 일부로 제공되는 특성과 자신의 필요에 맞게 만드는 특성, 두 종류가 있습니다. CLR 특성은 serialization, 마샬링 및 COM 상호 운용성을 지원하기 위해 사용됩니다. CLR을 검색해 보면 매우 많은 특성이 있다는 것을 알 수 있습니다. 이 중 일부 특성은 어셈블리에 적용되고, 다른 일부는 클래스 또는 인터페이스에 적용됩니다. 이러한 적용 대상을 특성 대상이라고 합니다.
 특성은 대상 항목 바로 앞에 두고 대괄호로 묶으면 해당 대상에 적용됩니다. 특성은 다음과 같이 다른 특성 위에 두거나,
[assembly: AssemblyDelaySign(false)] [assembly: AssemblyKeyFile(".\\keyFile.snk")]
여러 특성을 콤마로 구분함으로써 조합할 수도 있습니다.
[assembly: AssemblyDelaySign(false), assembly: AssemblyKeyFile(".\\keyFile.snk")]

맨 위로


사용자 지정 특성
 사용자 지정 특성을 자유롭게 만들고 적당한 때 런타임에 사용할 수 있습니다. 예를 들어, 코드의 섹션을 연결된 문서의 URL로 태그를 추가하는 문서 특성을 만들 수 있습니다. 또는, 코드를 코드 검토 주석이나 버그 수정 주석으로 태그를 추가할 수 있습니다.
 개발팀에서 버그 수정에 대한 기록을 유지하려는 경우를 생각해 보겠습니다. 모든 버그에 대한 데이터베이스를 유지하기 위해, 코드의 특정 수정 사항에 대해 버그 보고서를 연관시키려고 할 경우에는 코드에 다음과 같은 주석을 추가할 수 있을 것입니다.
// Bug 323 fixed by Jesse Liberty 1/1/2005.
 이렇게 해두면 소스 코드에서 쉽게 볼 수는 있겠지만, 이러한 정보를 보고서로 추출하거나 데이터베이스로 만들어 검색할 수 있도록 하면 더 좋을 것입니다. 또한 모든 버그 보고서 표시 방식을 동일한 구문으로 사용하면 좋을 것입니다. 이 때 필요한 것이 사용자 지정 특성입니다. 그러기 위해서는 주석을 다음과 같이 바꿀 수 있습니다.
[BugFix(323,"Jesse Liberty","1/1/2005") Comment="Off by one error"]
 특성도 C#의 클래스에 해당합니다. 사용자 지정 특성을 만들려면 System.Attribute에서 파생된 새로운 사용자 지정 특성을 만듭니다.
public class BugFixAttribute : System.Attribute
컴파일러에게 이 특성이 어떤 종류의 요소(특성 대상)와 함께 사용될 수 있는지 말해 주어야 합니다. 이것을 특성으로 지정합니다. (너무 당연한가요?)
[AttributeUsage(AttributeTargets.ClassMembers, AllowMultiple = true)]
AttributeUsage는 특성에 적용되는 특성(메타 특성)입니다. 이것은 메타 메타데이터, 즉 메타데이터에 대한 데이터를 제공합니다. 이 경우는 두 개의 인수를 전달하게 되는데, 하나는 대상(이 경우는 클래스 멤버)이고 다른 하나는 주어진 요소가 이러한 특성을 둘 이상 받을 수 있는지 여부를 나타내는 플래그입니다. AllowMultiple은 true로 설정되어 있는데, 이것은 클래스 멤버에 둘 이상의 BugFixAttribute가 지정될 수 있음을 의미합니다.
 Attribute 대상을 조합하려는 경우에는 OR를 사용할 수 있습니다.
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface, AllowMultiple = true)]
이것은 특성이 Class 또는 Interface에 연결될 수 있도록 합니다.
 새로운 사용자 지정 특성의 이름은 BugFixAttribute입니다. 표기법은 단어 Attribute를 해당 특성 이름에 붙이는 것입니다. 특성을 요소에 지정하면, 컴파일러에서 특성을 짧은 버전의 이름으로 호출할 수 있도록 지원합니다. 따라서 다음과 같이 작성할 수 있습니다.
[BugFix(123, "Jesse Liberty", "01/01/05", Comment="Off by one")]
컴파일러는 먼저 BugFix라는 이름의 특성을 찾고, 이것을 찾지 못할 경우 BugFixAttribute를 찾습니다.
 모든 특성은 적어도 하나의 생성자를 가지고 있어야 합니다. 특성은 두 가지의 형식의 매개 변수를 가지는데, 하나는 위치 매개 변수이고 다른 하나는 이름 매개 변수입니다. 이전 예제에서 버그 ID, 프로그래머 이름 및 날짜는 위치 매개 변수이고, 주석은 이름 매개 변수에 해당합니다. 위치 매개 변수는 생성자를 통해 전달되고 생성자에서 선언된 순서대로 전달되어야 합니다.
public BugFixAttribute(int bugID, string programmer, string date) { this.bugID = bugID; this.programmer = programmer; this.date = date; }
이름 매개 변수는 속성과 같이 구현됩니다.

맨 위로


특성 사용
 특성을 테스트하기 위해 MyMath라는 이름의 간단한 클래스를 만들고 두 개의 함수를 부여합니다. 그런 다음, 클래스에 버그 수정 특성을 지정합니다.
[BugFixAttribute(121,"Jesse Liberty","01/03/05")] [BugFixAttribute(107,"Jesse Liberty","01/04/05", Comment="Fixed off by one errors")] public class MyMath
이러한 특성은 메타데이터와 함께 저장됩니다. 그림 6에 전체 소스 코드가 제공되어 있습니다. 다음은 출력입니다.
Calling DoFunc(7). Result: 9.3333333333333339
보는 바와 같이, 특성은 출력에 대해 전혀 영향을 미치지 않고, 특성을 만들어도 성능에는 아무런 영향이 없습니다. 사실, 특성이 존재한다는 사실도 제 말을 통해 믿을 수 밖에 없을 것입니다. 하지만 그림 7에서 보는 바와 같이 ILDASM을 사용한 메타데이터를 잠깐 살펴보면 특성이 있다는 사실을 알 수 있습니다.

맨 위로


검토
 검토가 유용하려면, 이상적으로는 런타임 동안 메타데이터에서 특성에 액세스하는 방법이 필요합니다. C#은 메타데이터 검사를 위한 검토를 지원합니다. 먼저 MemberInfo 형식의 개체를 초기화합니다. System.Reflection 네임스페이스의 이 개체는 멤버의 특성을 발견하고 메타데이터에 대한 액세스를 위해 제공합니다.
System.Reflection.MemberInfo inf = typeof(MyMath);
MyMath 형식에 대해 typeof 연산자를 호출합니다. 그러면 MemberInfo에서 파생된 형식 Type의 개체가 반환됩니다.
 다음 단계는 이 MemberInfo 개체에 대해 GetCustomAttributes를 호출하여 찾고자 하는 특성의 형식을 전달하는 것입니다. 이렇게 되면 개체 배열을 얻을 수 있으며, 여기서 이들 각 개체는 형식 BugFixAttribute에 속합니다.
object[] attributes; attributes = Attribute.GetCustomAttributes(inf, typeof(BugFixAttribute));
 이제 그림 8과 같이 이 배열에 걸쳐 반복하여 BugFixAttribute 개체의 속성을 출력할 수 있습니다. 이 대체 코드를 그림 6의 코드에 삽입하면 메타데이터가 표시됩니다.

맨 위로


형식 찾기
 검토를 사용하여 어셈블리의 내용을 찾고 검사할 수 있습니다. 어셈블리에 대한 정보를 표시할 도구를 만들거나 어셈블리에서 메서드를 동적으로 호출하려는 경우 특히 유용합니다. 스크립팅 엔진을 개발하여 사용자가 스크립트를 생성하고 프로그램을 통해 실행하도록 하려는 경우에도 해당합니다.
 검토를 통해, 모듈과 연관된 형식, 메서드, 필드, 속성 및 이벤트와 연관된 형식은 물론이고, 각 형식 메서드의 서명, 형식에 의해 지원되는 인터페이스 및 형식의 슈퍼클래스도 찾을 수 있습니다.
 우선, 어셈블리를 Assembly.Load 정적 메서드로 동적으로 로드합니다. 이 메서드에 대한 서명은 다음과 같습니다.
public static Assembly.Load(AssemblyName)
그런 다음, 코어 라이브러리를 전달해야 합니다.
Assembly a = Assembly.Load("Mscorlib.dll");
 어셈블리가 로드되었으면 GetTypes를 호출하여 Type 개체의 배열을 반환할 수 있습니다. Type 개체는 검토의 핵심입니다. Type은 형식 선언, 즉 클래스, 인터페이스, 배열, 값 및 열거를 나타냅니다.
Type[] types = a.GetTypes();
 어셈블리는 foreach 루프에 표시될 수 있는 형식의 배열을 반환합니다. 여기에서의 출력으로 많은 페이지가 채워집니다. 다음은 출력의 일부입니다.
Type is System.TypeCode Type is System.Security.Util.StringExpressionSet Type is System.Text.UTF7Encoding$Encoder Type is System.ArgIterator Type is System.Runtime.Remoting.JITLookupTable 1205 types found
코어 라이브러리의 형식으로 가득 찬 배열을 얻었고 이것을 하나씩 출력했습니다. 출력에서 보는 바와 같이 배열에는 1,205개 항목이 포함되어 있습니다.

맨 위로


형식에 대한 검토
 어셈블리의 단일 형식에 대해서도 검토할 수 있습니다. 이렇게 하려면, GetType 메서드로 어셈블리에서 한 형식을 추출합니다.
public class Tester { public static void Main() { // examine a single object Type theType = Type.GetType("System.Reflection.Assembly"); Console.WriteLine("\nSingle Type is {0}\n", theType); } }
출력은 다음과 같습니다.
Single Type is System.Reflection.Assembly

맨 위로


멤버 찾기
 그림 9에서 보는 바와 같이, 모든 해당 멤버에 대해 이 형식을 요청하여 모든 메서드, 속성 및 필드를 나열할 수 있습니다.
출력이 상당히 길어지긴 했지만 다음 출력의 일부에서 확인할 수 있듯이 출력 안에서 필드, 메서드, 생성자 및 속성을 볼 수 있습니다.
System.String s_localFilePrefix is a Field Boolean IsDefined(System.Type) is a Method Void .ctor() is a Constructor System.String CodeBase is a Property System.String CopiedCodeBase is a Property

맨 위로


메서드만 찾기
 필드, 속성 등은 제외하고 메서드에 대해서만 관심이 있을 수 있습니다. 이렇게 하려면, GetMembers에 대한 호출을 제거합니다.
MemberInfo[] mbrInfoArray = theType.GetMembers(BindingFlags.LookupAll);
그런 다음, GetMethods에 대한 호출을 추가합니다.
mbrInfoArray = theType.GetMethods();
이제 출력에는 메서드밖에 없습니다.
Output (excerpt) Boolean Equals(System.Object) is a Method System.String ToString() is a Method System.String CreateQualifiedName(System.String, System.String) is a Method System.Reflection.MethodInfo get_EntryPoint() is a Method

맨 위로


특정 멤버 찾기
 마지막으로, 더욱 범위를 좁히기 위해 FindMembers 메서드를 사용하여 형식의 특정 멤버를 찾을 수 있습니다. 예를 들어, 그림 10에서 보는 바와 같이 문자 "Get"으로 이름이 시작하는 메서드에 대한 검색으로 제한할 수 있습니다.
 출력의 일부는 다음과 같습니다.
System.Type[] GetTypes() is a Method System.Type[] GetExportedTypes() is a Method System.Type GetType(System.String, Boolean) is a Method System.Type GetType(System.String) is a Method System.Reflection.AssemblyName GetName(Boolean) is a Method System.Reflection.AssemblyName GetName() is a Method Int32 GetHashCode() is a Method System.Reflection.Assembly GetAssembly(System.Type) is a Method System.Type GetType(System.String, Boolean, Boolean) is a Method

맨 위로


동적 호출
 메서드를 찾았으면 검토를 사용하여 호출할 수 있습니다. 예를 들어, 각도의 코사인 값을 반환하는 System.Math의 Cos 메서드를 호출하려는 경우를 생각해 보겠습니다.
 이렇게 하려면, 다음과 같이 System.Math 클래스에 대한 Type 정보를 얻습니다.
Type theMathType = Type.GetType("System.Math");
 이 형식 정보로 해당 클래스의 인스턴스를 동적으로 로드할 수 있습니다.
Object theObj = Activator.CreateInstance(theMathType);
CreateInstance는 개체 인스턴스화를 위해 사용할 수 있는 Activator 클래스의 정적 메서드입니다.
 System.Math의 인스턴스가 있으면 Cos 메서드를 호출할 수 있습니다. 이렇게 하려면, 매개 변수의 형식을 설명하는 배열을 준비해야 합니다. Cos는 하나의 매개 변수(코사인 값이 필요한 각도)를 가지므로 하나의 멤버를 가진 배열이 필요합니다. 이 배열에 Cos에서 예상하는 매개 변수 형식인 System.Double 형식에 대한 Type 개체를 둡니다.
Type[] paramTypes = new Type[1]; paramTypes[0]= Type.GetType("System.Double");
 이제 원하는 메서드의 이름과 이전에 얻은 형식 개체의 GetMethod 메서드에게 매개 변수의 형식을 설명하는 이 배열을 전달할 수 있습니다.
MethodInfo CosineInfo = theMathType.GetMethod("Cos",paramTypes);
 이제 메서드를 호출할 수 있는 형식 MethodInfo의 개체를 가지고 있습니다. 메서드를 호출하려면, 배열에서 다시 매개 변수의 실제 값을 전달해야 합니다.
Object[] parameters = new Object[1]; parameters[0] = 45; Object returnVal = CosineInfo.Invoke(theObj,parameters);
 이제 두 개의 배열을 만들었습니다. 하나는 매개 변수의 형식을 가진 paramTypes이고, 다른 하나는 실제 값을 가진 매개 변수입니다. 메서드가 두 개의 인수를 가지고 있는 경우 이러한 배열이 두 값을 가지도록 선언하면 됩니다. 메서드가 값을 가지고 있지 않은 경우에도 배열을 만들 수 있지만, 크기를 0으로 부여해야 합니다.
Type[] paramTypes = new Type[0];
조금 이상하게 보이지만 이것이 맞습니다. 그림 11에 전체 코드가 나와 있습니다.

맨 위로


결론
 부주의한 C++ 프로그래머가 혼란스러워 할 수 있는 몇 가지 함정이 있기는 하지만 C#의 구문은 C++과 크게 다르지 않고 새로운 언어로의 전환은 비교적 쉽다고 볼 수 있습니다. C#으로 작업하면서 느끼는 흥미로운 점은, 이전에 직접 작성해야 했던 많은 기능을 제공하는 새로운 공용 언어 런타임 라이브러리를 항상 사용하게 된다는 점입니다. 이 글에서는 몇 가지 눈에 띄는 사항만 간단히 다루었습니다. CLR 및 .NET Framework는 스레드, 마샬링, 웹 응용 프로그램 개발, Windows 기반 응용 프로그램 개발 등을 위한 폭넓은 기능을 지원합니다.
 언어 기능과 CLR 기능간의 구분이 모호할 때가 많은 것이 사실이지만, 이 두 가지는 분명 강력한 개발 도구인 것입니다.

? 최종수정일: 2003년 1월 6일

반응형

'프로그래밍(Programming) > C#' 카테고리의 다른 글

C# 프로그래밍 가이드 - microsoft  (0) 2015.08.25
C# station 괜찮은 사이트  (0) 2013.06.07
C# 일관성 없는 액세스 가능성  (0) 2013.01.22
C#강의 51~55 끝  (0) 2012.10.31
c#강의 41~50  (0) 2012.10.31

+ Recent posts