반응형

Hashtable 클래스



구문

[SerializableAttribute]
[ComVisibleAttribute(true)]
public class Hashtable : IDictionary, ICollection, IEnumerable, 
	ISerializable, IDeserializationCallback, ICloneable




Hashtable 의 키 값은 숫자도 가능하지만 문자열 및 오브젝트도 동시에 가능합니다
즉 구분이 될 수 있는 것들은 가능하지만, null 은 불가능합니다
또한 해쉬 테이블은 저장되는 순서가 보장되지 않습니다



초기화 하 고 다양 한 기능을 수행 하는 방법을 보여 주는 Hashtable 및 해당 키와 값을 출력 하는 방법입니다.

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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
using System;
using System.Collections;
 
class Example
{
    public static void Main()
    {
        // Create a new hash table.
        //
        Hashtable openWith = new Hashtable();
 
        // Add some elements to the hash table. There are no 
        // duplicate keys, but some of the values are duplicates.
        openWith.Add("txt""notepad.exe");
        openWith.Add("bmp""paint.exe");
        openWith.Add("dib""paint.exe");
        openWith.Add("rtf""wordpad.exe");
 
        // The Add method throws an exception if the new key is 
        // already in the hash table.
        try
        {
            openWith.Add("txt""winword.exe");
        }
        catch
        {
            Console.WriteLine("An element with Key = \"txt\" already exists.");
        }
 
        // The Item property is the default property, so you 
        // can omit its name when accessing elements. 
        Console.WriteLine("For key = \"rtf\", value = {0}.", openWith["rtf"]);
 
        // The default Item property can be used to change the value
        // associated with a key.
        openWith["rtf"= "winword.exe";
        Console.WriteLine("For key = \"rtf\", value = {0}.", openWith["rtf"]);
 
        // If a key does not exist, setting the default Item property
        // for that key adds a new key/value pair.
        openWith["doc"= "winword.exe";
 
        // ContainsKey can be used to test keys before inserting 
        // them.
        if (!openWith.ContainsKey("ht"))
        {
            openWith.Add("ht""hypertrm.exe");
            Console.WriteLine("Value added for key = \"ht\": {0}", openWith["ht"]);
        }
 
        // When you use foreach to enumerate hash table elements,
        // the elements are retrieved as KeyValuePair objects.
        Console.WriteLine();
        foreach (DictionaryEntry de in openWith)
        {
            Console.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value);
        }
 
        // To get the values alone, use the Values property.
        ICollection valueColl = openWith.Values;
 
        // The elements of the ValueCollection are strongly typed
        // with the type that was specified for hash table values.
        Console.WriteLine();
        foreach (string s in valueColl)
        {
            Console.WriteLine("Value = {0}", s);
        }
 
        // To get the keys alone, use the Keys property.
        ICollection keyColl = openWith.Keys;
 
        // The elements of the KeyCollection are strongly typed
        // with the type that was specified for hash table keys.
        Console.WriteLine();
        foreach (string s in keyColl)
        {
            Console.WriteLine("Key = {0}", s);
        }
 
        // Use the Remove method to remove a key/value pair.
        Console.WriteLine("\nRemove(\"doc\")");
        openWith.Remove("doc");
 
        if (!openWith.ContainsKey("doc"))
        {
            Console.WriteLine("Key \"doc\" is not found.");
        }
 
        //
        //openWith.Add(0, "wordpad.exe11");
        openWith[0= "wordpad.exe11";
 
        //키 값은 null 이 될 수 없지만 값은 null 이 될 수 있습니다
        //openWith.Add(null, "wordpad.exe11");        //error : 'Key cannot be null.'
        //openWith.Add(0, null);
 
        object t1 = new object();
        object t2 = new object();
        openWith.Add(t1, "sdfsdf");
        openWith.Add(t2, "sdfsdf123");
        Console.WriteLine(openWith[t1]);
        Console.WriteLine(openWith[t2]);
 
        Console.WriteLine(openWith[0]);
 
 
        Console.WriteLine();
        foreach (DictionaryEntry de in openWith)
        {
            Console.WriteLine("Key = {0}, Value = {1}", de.Key, de.Value);
        }
 
 
    }
}
 
 
/*
An element with Key = "txt" already exists.
For key = "rtf", value = wordpad.exe.
For key = "rtf", value = winword.exe.
Value added for key = "ht": hypertrm.exe
Key = txt, Value = notepad.exe
Key = doc, Value = winword.exe
Key = ht, Value = hypertrm.exe
Key = bmp, Value = paint.exe
Key = dib, Value = paint.exe
Key = rtf, Value = winword.exe
Value = notepad.exe
Value = winword.exe
Value = hypertrm.exe
Value = paint.exe
Value = paint.exe
Value = winword.exe
Key = txt
Key = doc
Key = ht
Key = bmp
Key = dib
Key = rtf
Remove("doc")
Key "doc" is not found.
sdfsdf
sdfsdf123
wordpad.exe11
Key = System.Object, Value = sdfsdf123
Key = txt, Value = notepad.exe
Key = System.Object, Value = sdfsdf
Key = ht, Value = hypertrm.exe
Key = bmp, Value = paint.exe
Key = dib, Value = paint.exe
Key = rtf, Value = winword.exe
Key = 0, Value = wordpad.exe11
*/

cs



이름설명
System_CAPS_pubmethodHashtable()

비어 있는 새 인스턴스를 초기화는 Hashtable 기본 초기 용량을 사용 하 여 클래스, 로드 비율, 해시 코드 공급자 및 비교자입니다.

System_CAPS_pubmethodHashtable(IDictionary)

새 인스턴스를 초기화는 Hashtable 지정된 된 사전에서 새 요소를 복사 하 여 클래스 Hashtable 개체입니다. 새 Hashtable 개체는 복사 된 요소 수와 동일한 초기 용량을 갖고와 기본 로드 비율, 해시 코드 공급자 및 비교자를 사용 하 여 합니다.

System_CAPS_pubmethodHashtable(IDictionary, IEqualityComparer)

새 인스턴스를 초기화는 Hashtable 지정된 된 사전에서 새 요소를 복사 하 여 클래스 Hashtable 개체입니다. 새 Hashtable 개체는 복사 된 요소 수와 동일한 초기 용량을 갖고와 기본 로드 비율 및 지정 된 사용 하 여 IEqualityComparer 개체입니다.

System_CAPS_pubmethodHashtable(IDictionary, IHashCodeProvider, IComparer)

사용되지 않습니다. 새 인스턴스를 초기화는 Hashtable 지정된 된 사전에서 새 요소를 복사 하 여 클래스 Hashtable 개체입니다. 새 Hashtable 개체는 복사 된 요소 수와 동일한 초기 용량을 갖고와 기본 로드 비율과 지정 된 해시 코드 공급자 및 비교자를 사용 하 여 합니다. 이 API는 더 이상 사용되지 않습니다. 다른 방법에 대 한 참조 Hashtable합니다.

System_CAPS_pubmethodHashtable(IDictionary, Single)

새 인스턴스를 초기화는 Hashtable 지정된 된 사전에서 새 요소를 복사 하 여 클래스 Hashtable 개체입니다. 새 Hashtable 개체는 복사 된 요소 수와 동일한 초기 용량을 갖고 및 지정 된 로드 비율과 기본 해시 코드 공급자 및 비교자를 사용 하 여 합니다.

System_CAPS_pubmethodHashtable(IDictionary, Single, IEqualityComparer)

새 인스턴스를 초기화는 Hashtable 지정된 된 사전에서 새 요소를 복사 하 여 클래스 Hashtable 개체입니다. 새 Hashtable 개체는 복사 된 요소 수와 동일한 초기 용량을 갖고 및 지정 된 로드 비율을 사용 하 고 IEqualityComparer 개체입니다.

System_CAPS_pubmethodHashtable(IDictionary, Single, IHashCodeProvider, IComparer)

사용되지 않습니다. 새 인스턴스를 초기화는 Hashtable 지정된 된 사전에서 새 요소를 복사 하 여 클래스 Hashtable 개체입니다. 새 Hashtable 개체 초기 용량이 복사 된 요소 수와 동일 하 고 지정된 로드 비율, 해시 코드 공급자 및 비교자를 사용 합니다.

System_CAPS_pubmethodHashtable(IEqualityComparer)

비어 있는 새 인스턴스를 초기화는 Hashtable 기본 초기 용량을 사용 하 여 클래스 및 로드 비율과 지정 된 IEqualityComparer 개체입니다.

System_CAPS_pubmethodHashtable(IHashCodeProvider, IComparer)

사용되지 않습니다. 비어 있는 새 인스턴스를 초기화는 Hashtable 기본 초기 용량을 사용 하 여 클래스 및 로드 비율과 지정 된 해시 코드 공급자 및 비교자입니다.

System_CAPS_pubmethodHashtable(Int32)

비어 있는 새 인스턴스를 초기화는 Hashtable 지정된 된 초기 용량을 기본 로드 비율, 해시 코드 공급자 및 비교자를 사용 하 여 클래스입니다.

System_CAPS_pubmethodHashtable(Int32, IEqualityComparer)

비어 있는 새 인스턴스를 초기화는 Hashtable 지정된 된 초기 용량을 사용 하 여 클래스 및 IEqualityComparer, 및 기본 로드 비율입니다.

System_CAPS_pubmethodHashtable(Int32, IHashCodeProvider, IComparer)

사용되지 않습니다. 비어 있는 새 인스턴스를 초기화는 Hashtable 지정된 된 초기 용량, 해시 코드 공급자, 비교자 및 기본 로드 비율을 사용 하 여 클래스입니다.

System_CAPS_pubmethodHashtable(Int32, Single)

비어 있는 새 인스턴스를 초기화는 Hashtable 지정된 된 초기 용량을 사용 하 여 클래스 및 로드 비율과 기본 해시 코드 공급자 및 비교자입니다.

System_CAPS_pubmethodHashtable(Int32, Single, IEqualityComparer)

비어 있는 새 인스턴스를 초기화는 Hashtable 지정 된 초기 용량, 로드 비율을 사용 하 여 클래스 및 IEqualityComparer 개체입니다.

System_CAPS_pubmethodHashtable(Int32, Single, IHashCodeProvider, IComparer)

사용되지 않습니다. 비어 있는 새 인스턴스를 초기화는 Hashtable 지정된 된 초기 용량을 사용 하 여 클래스, 로드 비율, 해시 코드 공급자 및 비교자입니다.

System_CAPS_protmethodHashtable(SerializationInfo, StreamingContext)

비어 있는 새 인스턴스를 초기화는 Hashtable 클래스를 사용 하 여 지정 된 직렬화 가능 SerializationInfo및 StreamingContext 개체입니다.

이름설명
System_CAPS_protpropertycomparer

사용되지 않습니다. 가져오거나는 IComparer 를 사용 하는 Hashtable합니다.

System_CAPS_pubpropertyCount

Hashtable에 포함된 키/값 쌍의 수를 가져옵니다.

System_CAPS_protpropertyEqualityComparer

가져옵니다는 IEqualityComparer 를 사용 하는 Hashtable합니다.

System_CAPS_protpropertyhcp

사용되지 않습니다. 해시 코드를 분배할 수 있는 개체를 가져오거나 설정합니다.

System_CAPS_pubpropertyIsFixedSize

Hashtable의 크기가 고정되어 있는지를 나타내는 값을 가져옵니다.

System_CAPS_pubpropertyIsReadOnly

Hashtable가 읽기 전용인지 여부를 나타내는 값을 가져옵니다.

System_CAPS_pubpropertyIsSynchronized

Hashtable에 대한 액세스가 동기화되어 스레드로부터 안전하게 보호되는지를 나타내는 값을 가져옵니다.

System_CAPS_pubpropertyItem[Object]

지정된 키에 연결된 값을 가져오거나 설정합니다.

System_CAPS_pubpropertyKeys

가져옵니다는 ICollection 키를 포함 하는 Hashtable합니다.

System_CAPS_pubpropertySyncRoot

Hashtable에 대한 액세스를 동기화하는 데 사용할 수 있는 개체를 가져옵니다.

System_CAPS_pubpropertyValues

ICollection의 값이 들어 있는 Hashtable을 가져옵니다.

이름설명
System_CAPS_pubmethodAdd(Object, Object)

지정한 키와 값을 가지는 요소를 Hashtable에 추가합니다.

System_CAPS_pubmethodClear()

Hashtable에서 요소를 모두 제거합니다.

System_CAPS_pubmethodClone()

Hashtable의 부분 복사본을 만듭니다.

System_CAPS_pubmethodContains(Object)

Hashtable에 특정 키가 들어 있는지 여부를 확인합니다.

System_CAPS_pubmethodContainsKey(Object)

Hashtable에 특정 키가 들어 있는지 여부를 확인합니다.

System_CAPS_pubmethodContainsValue(Object)

Hashtable에 특정 값이 들어 있는지 여부를 확인합니다.

System_CAPS_pubmethodCopyTo(Array, Int32)

복사본은 Hashtable 요소를 1 차원 Array 인스턴스의 지정한 인덱스에 있습니다.

System_CAPS_pubmethodEquals(Object)

지정한 개체와 현재 개체가 같은지 여부를 확인합니다.(Object에서 상속됨)

System_CAPS_protmethodFinalize()

가비지 컬렉션이 회수하기 전에 개체가 리소스를 해제하고 다른 정리 작업을 수행할 수 있게 합니다.(Object에서 상속됨)

System_CAPS_pubmethodGetEnumerator()

반환 된 IDictionaryEnumerator 을 반복 하는 Hashtable합니다.

System_CAPS_protmethodGetHash(Object)

지정한 키의 해시 코드를 반환합니다.

System_CAPS_pubmethodGetHashCode()

기본 해시 함수로 작동합니다.(Object에서 상속됨)

System_CAPS_pubmethodGetObjectData(SerializationInfo, StreamingContext)

구현 하는 ISerializable 인터페이스를 serialize 하는 데 필요한 데이터를 반환 된 Hashtable합니다.

System_CAPS_pubmethodGetType()

현재 인스턴스의 Type을 가져옵니다.(Object에서 상속됨)

System_CAPS_protmethodKeyEquals(Object, Object)

특정 비교 Object 에 특정 키로는 Hashtable합니다.

System_CAPS_protmethodMemberwiseClone()

현재 Object의 단순 복사본을 만듭니다.(Object에서 상속됨)

System_CAPS_pubmethodOnDeserialization(Object)

ISerializable 인터페이스를 구현하고, deserialization이 완료되면 deserialization 이벤트를 발생시킵니다.

System_CAPS_pubmethodRemove(Object)

Hashtable에서 지정한 키를 가지는 요소를 제거합니다.

System_CAPS_pubmethodSystem_CAPS_staticSynchronized(Hashtable)

동기화 (스레드로부터 안전한 지) 래퍼를 반환 된 Hashtable합니다.

System_CAPS_pubmethodToString()

현재 개체를 나타내는 문자열을 반환합니다.(Object에서 상속됨)

이름설명
System_CAPS_pubinterfaceSystem_CAPS_privmethodIEnumerable.GetEnumerator()

컬렉션을 반복하는 열거자를 반환합니다.

이름설명
System_CAPS_pubmethodAsParallel()

오버로드되었습니다. 쿼리를 병렬화할 수 있도록 합니다.(ParallelEnumerable에서 정의됨)

System_CAPS_pubmethodAsQueryable()

오버로드되었습니다. 변환 된 IEnumerable 에 IQueryable합니다.(Queryable에서 정의됨)

System_CAPS_pubmethodCast<TResult>()

요소에 캐스트는 IEnumerable 지정 된 형식입니다.(Enumerable에서 정의됨)

System_CAPS_pubmethodOfType<TResult>()

요소를 필터링 한 IEnumerable 지정된 된 형식에 기반 합니다.(Enumerable에서 정의됨)


ref : https://msdn.microsoft.com/ko-kr/library/system.collections.hashtable(v=vs.110).aspx




반응형
반응형



CLR 은 VS 에서 컴파일된 중간 언어(IL = MSIL) 을 다시한번 재 컴파일해 각 플랫폼에서 실행 될 수 있는 네이티브 어셈블리로 변환하고 실행하는 역할을 합니다, 여러 플랫폼에서 실행 될 수 있게 하기위한 전략인 것이죠(닷넷 프레임 워크가 존재하는 이유중 하나라고 할 수 있습니다)

CLR 은 닷넷 프레임 워크 안에 존재하며 이것을 프로그래머가 수정하거나 가져다 쓸 수는 없습니다



.NET Framework 플랫폼 아키텍처

C# 프로그램은 CLR(공용 언어 런타임)이라고 하는 가상 실행 시스템과 통합된 클래스 라이브러리 집합을 포함하는 Windows의 통합 구성 요소인 .NET Framework에서 실행됩니다. CLR은 언어 및 라이브러리가 원활하게 함께 작동하는 실행 및 개발 환경을 만들기 위한 기준이 되는 국제 표준인 CLI(공용 언어 인프라)를 Microsoft에서 상업적으로 구현한 것입니다.

C#으로 작성된 소스 코드는 CLI 사양을 준수하는 IL(중간 언어)로 컴파일됩니다. IL 코드 및 리소스(예: 비트맵 및 문자열)는 일반적으로 확장명이 .exe 또는 .dll인 어셈블리라는 실행 파일로 디스크에 저장됩니다. 어셈블리는 어셈블리의 형식, 버전, 문화권 및 보안 요구 사항에 대한 정보를 제공하는 매니페스트를 포함합니다.

C# 프로그램이 실행될 경우 어셈블리가 CLR에 로드되어 매니페스트의 정보를 기준으로 다양한 작업을 수행할 수 있습니다. 그런 다음 보안 요구 사항을 충족되면 CLR은 JIT(Just-In-Time) 컴파일을 수행하여 IL 코드를 네이티브 기계어 명령으로 변환합니다. 

또한 CLR은 자동 가비지 수집, 예외 처리 및 리소스 관리와 관련된 다른 서비스도 제공합니다. 

CLR에서 실행되는 코드는 "관리 코드"라고도 합니다. 

즉, 특정 시스템을 대상으로 하는 네이티브 기계어로 컴파일되는 "비관리 코드"와는 반대됩니다. 다음 다이어그램은 C# 소스 코드 파일, .NET Framework 클래스 라이브러리, 어셈블리 및 CLR의 컴파일 타임 및 런타임 관계를 보여 줍니다.


CLR 로 넘어 가는 것은 MSIL 이며 CLR 안의 JIT 컴파일러를 통해 네이트브 기계어 코드로 변환 됩니다


언어 상호 운용성은 .NET Framework의 주요 기능입니다. 

C# 컴파일러에서 생성된 IL 코드는 CTS(공용 형식 사양=표준 타입 규칙)을 준수하므로 

C#에서 생성된 IL 코드는 .NET 버전의 Visual Basic, Visual C++ 또는 20개 이상의 다른 CTS 규격(닷넷 호환 언어가 지켜야할 타입의 표준 규칙) 언어에서 생성된 코드와 상호 작용할 수 있습니다. 

단일 어셈블리는 다른 .NET 언어로 작성된 여러 모듈을 포함할 수 있고 형식은 마치 같은 언어로 작성된 것처럼 서로를 참조할 수 있습니다.

.NET Framework에는 런타임 서비스 외에, 파일 입/출력부터 문자열 조작, XML 구문 분석, Windows Forms 컨트롤에 이르는 모든 항목에 대해 다양하고 유용한 기능을 제공하는 네임스페이스로 구성된 4,000개 이상의 광범위한 클래스 라이브러리도 포함됩니다. 일반적인 C# 응용 프로그램은 .NET Framework 클래스 라이브러리를 광범위하게 사용하여 일반적인 "배관" 작업을 처리합니다.


ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/getting-started/introduction-to-the-csharp-language-and-the-net-framework

반응형
반응형

인덱서


인덱서는 프로퍼티( get{} set{} )를 확장한 형태와 유사합니다(거의 동일 this[..] 만 빼면)


인덱서는 배열 인덱싱 처럼 원소를 가져오거나 담을 수 있다는 뜻입니다


주로 멤버 배열 변수를 캡슐화 하기 위해 사용됩니다







인덱서는 클라이언트 응용 프로그램에서 배열과 마찬가지 방식으로 액세스할 수 있는 클래스구조체 또는 인터페이스를 만들 수 있게 해 주는 편리한 구문입니다. 인덱서는 주로 내부 컬렉션 또는 배열을 캡슐화하기 위한 형식에서 가장 많이 구현됩니다. 예를 들어 24시간 동안 10번 기록되는 온도를 화씨로 나타내는 TempRecord라는 클래스가 있다고 가정합니다. 클래스에는 온도를 나타내는 float 형식의 "temps"라는 배열과 온도를 기록한 날짜를 나타내는 DateTime이 들어 있습니다. 이 경우 인덱서를 구현하면 클라이언트는 float temp = tr.temps[4] 대신 float temp = tr[4]로 TempRecord 인스턴스의 온도에 액세스할 수 있습니다. 인덱서 표기법은 클라이언트 응용 프로그램의 구문을 단순하게 할 뿐 아니라 클래스와 해당 클래스의 용도를 다른 개발자가 쉽게 이해할 수 있도록 합니다.

클래스나 구조체에 대한 인덱서를 선언하려면 다음 예제에서와 같이 this 키워드를 사용합니다.

public int this[int index]    // Indexer declaration
{
    // get and set accessors
}




인덱서 형식과 해당 매개 변수 형식에는 적어도 인덱서 자체와 동등한 수준으로 액세스할 수 있어야 합니다. 액세스 가능성 수준에 대한 자세한 내용은 액세스 한정자를 참조하십시오.


인덱서 시그니처는 인덱서 정식 매개 변수의 수 및 형식으로 구성됩니다. 이 시그니처는 인덱서 형식 또는 정식 매개 변수 이름을 포함하지 않습니다. 같은 클래스에 두 개 이상의 인덱서를 선언한 경우, 모든 인덱서가 다른 시그니처를 가져야 합니다.

인덱서 값은 변수로 분류되지 않기 때문에 인덱서 값을 ref 또는 out 매개 변수로 전달할 수 없습니다.


예를 들어 다음과 같은 코드는 불가능 합니다

1
public float this[ref int index]
cs


위 코드는 다음과같은 에러를 발생시킵니다

오류 CS0631 이 컨텍스트에서는 ref 및 out을 사용할 수 없습니다.




다른 언어에서 사용할 수 있는 이름을 인덱서에 부여하려면 선언에 name 특성을 사용합니다. 예를 들면 다음과 같습니다.

[System.Runtime.CompilerServices.IndexerName("TheItem")]
public int this [int index]   // Indexer declaration
{
}

이 인덱서의 이름은 TheItem입니다. name 특성을 제공하지 않으면 기본 이름으로 Item이 사용됩니다.




인덱서 형식과 해당 매개 변수 형식에는 적어도 인덱서 자체와 동등한 수준으로 액세스할 수 있어야 합니다. 액세스 가능성 수준에 대한 자세한 내용은 액세스 한정자를 참조하십시오.



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
class TempRecord
{
    // Array of temperature values 
    private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F,
                                            61.3F, 65.9F, 62.1F, 59.2F, 57.5F };
 
    // To enable client code to validate input  
    // when accessing your indexer. 
    public int Length
    {
        get { return temps.Length; }
    }
    // Indexer declaration. 
    // If index is out of range, the temps array will throw the exception. 
    public float this[int index]
    {
        get
        {
            return temps[index];
        }
 
        set
        {
            temps[index] = value;
        }
    }
}
 
class MainClass
{
    static void Main()
    {
        TempRecord tempRecord = new TempRecord();
        // Use the indexer's set accessor
        tempRecord[3= 58.3F;
        tempRecord[5= 60.1F;
 
 
        // Use the indexer's get accessor 
        for (int i = 0; i < 10; i++)
        {
            System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
        }
 
    }
}
/* Output:
        Element #0 = 56.2
        Element #1 = 56.7
        Element #2 = 56.5
        Element #3 = 58.3
        Element #4 = 58.8
        Element #5 = 60.1
        Element #6 = 65.9
        Element #7 = 62.1
        Element #8 = 59.2
        Element #9 = 57.5
    */
 

cs



C#에서는 인덱스 형식이 정수로만 제한되지 않습니다. 

예를 들어, 인덱서와 함께 문자열을 사용하는 것이 유용할 수 있습니다. 

이러한 인덱서는 컬렉션에서 문자열을 검색하고 적절한 값을 반환하여 구현할 수 있습니다. 

접근자로 오버로드할 수 있으므로 문자열과 정수 버전의 인덱스 형식은 함께 사용될 수 있습니다.




string 형태의 인덱서



이 예제에서는 요일을 저장하는 클래스를 선언합니다. 문자열과 요일 이름을 가져오고 상응하는 정수를 반환하는 get 접근자를 선언합니다. 

예를 들어, 일요일은 0을 반환하고 월요일은 1을 반환하는 방식입니다.



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
// Using a string as an indexer value 
class DayCollection
{
    string[] days = { "Sun""Mon""Tues""Wed""Thurs""Fri""Sat" };
 
    // This method finds the day or returns -1 
    private int GetDay(string testDay)
    {
  //보다 효율적인 방법은 아니지만 예제를 위해서..
        for (int j = 0; j < days.Length; j++)
        {
            if (days[j] == testDay)
            {
                return j;
            }
        }
 // \" " 를 표현하기 위한 역슬래쉬
        throw new System.ArgumentOutOfRangeException(testDay, "testDay must be in the form \"Sun\", \"Mon\", etc");
    }
 
    // The get accessor returns an integer for a given string 
    public int this[string day]
    {
        get
        {
            return (GetDay(day));
        }
    }
}
 
class Program
{
    static void Main(string[] args)
    {
        DayCollection week = new DayCollection();
        System.Console.WriteLine(week["Fri"]);
 
        // Raises ArgumentOutOfRangeException
        System.Console.WriteLine(week["Made-up Day"]);
 
        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }
}
// Output: 5

cs







인터페이스 인덱서


interface(C# 참조) 에서 인덱서를 선언할 수 있습니다. 인터페이스 인덱서의 접근자와 클래스 인덱서의 접근자는 다음과 같은 차이점이 있습니다.

  • 인터페이스 접근자는 한정자를 사용하지 않습니다.

  • 인터페이스 접근자에는 본문이 없습니다.

따라서 접근자의 목적은 인덱서가 읽기/쓰기, 읽기 전용 또는 쓰기 전용인지 여부를 나타내는 것입니다.

다음은 인터페이스 인덱서 접근자의 예제입니다.


public interface ISomeInterface
{
    //... 

    // Indexer declaration: 
    string this[int index]
    {
        get;
        set;
    }
}




인터페이스 인덱서 구현 예


다음 예제는 인터페이스 인덱서의 구현 방법을 보여 줍니다.




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
// Indexer on an interface: 
    public interface ISomeInterface
    {
        // Indexer declaration: 
        int this[int index]
        {
            get;
            set;
        }
    }
 
    // Implementing the interface. 
    class IndexerClass : ISomeInterface
    {
        private int[] arr = new int[100];
        public int this[int index]   // indexer declaration
        {
            get
            {
                // 배열 범위 밖의 index 가 올 경우 크래쉬
                return arr[index];
            }
            set
            {
                arr[index] = value;
            }
        }
    }
 
    class MainClass
    {
        static void Main()
        {
            IndexerClass test = new IndexerClass();
            System.Random rand = new System.Random();
            // Call the indexer to initialize its elements. 
            for (int i = 0; i < 10; i++)
            {
                test[i] = rand.Next(); //랜던값 얻기
            }
            for (int i = 0; i < 10; i++)
            {
                System.Console.WriteLine("Element #{0} = {1}", i, test[i]);
            }
 
        }
    }
    /* Sample output:
        Element #0 = 360877544
        Element #1 = 327058047
        Element #2 = 1913480832
        Element #3 = 1519039937
        Element #4 = 601472233
        Element #5 = 323352310
        Element #6 = 1422639981
        Element #7 = 1797892494
        Element #8 = 875761049
        Element #9 = 393083859
     */

cs









ref : https://msdn.microsoft.com/ko-kr/library/2549tw02(v=vs.120).aspx

ref : https://msdn.microsoft.com/ko-kr/library/tkyhsw31(v=vs.120).aspx




반응형
반응형


named parameter


기초 문법이긴 한데 C++ 프로그래머에게는 낯설 수 있는 구문인 named parameter


별건 없고, 단지 좀 더 편의를 제공해 주는 기능 정도입니다


인자 값을넘겨 줄 때 변수명:값 형태로 넘겨 줄 수 있고 순서를 바꿔도 name 지정이기 때문에


넘어가는 것은 이름에 매칭되어 넘어갑니다



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
using System;
 
namespace Test
{
    public class Program
    {
 
        public void call(int first, int second)
        {
            Console.WriteLine(first+second);
        }
 
 
        static void Main()
        {
            int dd = 30;
            Program pi = new Program();
            pi.call(second: dd, first: 20);               //50
            pi.call(first: 20, second:2);                 //22
 
        }
 
    }
}
 







optional parameter 


는 C++ 에서 함수의 매개변수에 기본 값을 넣는 것과 동일하다고 생각하면 됩니다


가장 뒤의 매개변수 부터 채워져야 한다는 규칙을 갖고 있습니다





optional parameter 의 경우 오버로딩 함수들 중에서 어떤 함수로 호출될지를 변수 이름으로 지정할 수도 있습니다


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
using System;
 
namespace Test
{
    public class Program
    {
 
        public void call(int third)
        {
            Console.WriteLine(third);
        }
 
 
        public void call(int first, int second=30)
        {
            Console.WriteLine(first+second);
        }
 
        static void Main()
        {
            int dd = 30;
            Program pi = new Program();
            pi.call(second: dd, first: 20);                //50
            pi.call(first: 20, second:30);                 //22
 
 
            pi.call(30);            //public void call(int third) 이 호출 됨
 
            pi.call(first:30);      //public void call(int first, int second=30) 이 호출됨
 
            pi.call(third: 30);     //public void call(int third)   이 호출됨
 
        }
    }
}
 

cs




위 처럼 변수명:값 으로 오버로딩 함수중 하나를 택할 수 있지만 만약



가장 상단의 call 함수를 아래 처럼 변경한다면


        public void call(int first)

        {

            Console.WriteLine(first);

        }




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
using System;
 
namespace Test
{
    public class Program
    {
 
 
        public void call(int first)
        {
            Console.WriteLine(first);
        }
 
 
        public void call(int first, int second=30)
        {
            Console.WriteLine(first+second);
        }
 
 
        static void Main()
        {
            int dd = 30;
            Program pi = new Program();
            pi.call(second: dd, first: 20);                //50
            pi.call(first: 20, second:30);                 //22
 
 
            pi.call(30);                        //public void call(int third) 이 호출 됨
 
            pi.call(first: 30);                 //public void call(int first)   이 호출됨!!!!!!!!
 
            pi.call(first: 30, second:70);     //public void call(int first, int second = 30)   이 호출됨
 
pi.call( 30, second: 70); //public void call(int first, int second = 30) 이 호출됨

//pi.call(first: 30, 70); //7.2 버전부터..
 
        }
 
    }
}
 

cs


반응형
반응형



참조로 인수 전달 Ref

메서드의 매개 변수 목록에 사용되는 경우 ref 키워드는 인수가 값이 아니라 참조로 전달됨을 나타냅니다. 인수를 참조로 전달하는 경우 호출된 메서드의 인수 변경 내용이 호출 메서드에 반영됩니다. 예를 들어 호출자가 지역 변수 식 또는 배열 요소 액세스 식을 전달하는 경우 호출된 메서드에서 ref 매개 변수가 참조하는 개체를 바꾸면 메서드 반환 시 호출자의 지역 변수 또는 배열 요소가 새 개체를 참조합니다.



참고

참조로 전달의 개념과 참조 형식의 개념을 혼동해서는 안 됩니다. 이 두 개념은 서로 다릅니다. 메서드 매개 변수는 값 형식이든 참조 형식이든 관계없이 ref를 통해 수정할 수 있으며, 참조로 전달되는 경우 값 형식은 boxing되지 않습니다.

ref 매개 변수를 사용하려면 다음 예제에 나와 있는 것처럼 메서드 정의와 호출 메서드가 모두 ref 키워드를 명시적으로 사용해야 합니다.


void Method(ref int refArgument) { refArgument = refArgument + 44; } int number = 1; Method(ref number); Console.WriteLine(number); // Output: 45



ref 또는 in 매개 변수로 전달하는 인수는 전달 전에 초기화해야 합니다. 이러한 방식은 인수를 전달하기 전에 명시적으로 초기화할 필요가 없는 out 매개 변수와는 다릅니다.

클래스의 멤버는 refin 또는 out만 다른 서명을 포함할 수 없습니다. 특정 형식의 두 멤버가 하나는 ref 매개 변수를 포함하고 다른 하나는 out 또는 in 매개 변수를 포함한다는 것 외에는 차이가 없으면 컴파일러 오류가 발생합니다. 예를 들어 다음 코드는 컴파일되지 않습니다.

class CS0663_Example { // Compiler error CS0663: "Cannot define overloaded // methods that differ only on ref and out". public void SampleMethod(out int i) { } public void SampleMethod(ref int i) { } }



그러나 다음 예제에 나와 있는 것처럼 메서드 하나에는 refin 또는 out 매개 변수가 포함되어 있고 다른 하나에는 값 매개 변수가 포함되어 있으면 메서드를 오버로드할 수 있습니다.

class RefOverloadExample { public void SampleMethod(int i) { } public void SampleMethod(ref int i) { } }


참조로 인수 전달: 예제

앞의 예제에서는 값 형식을 참조로 전달합니다. ref 키워드를 사용하여 참조 형식을 참조로 전달할 수도 있습니다. 참조 형식을 참조로 전달하는 경우 호출된 메서드는 참조 매개 변수가 호출자에서 참조하는 개체를 바꿀 수 있습니다. 개체의 저장 위치는 참조 매개 변수의 값으로 메서드에 전달됩니다. 매개 변수의 저장 위치에서 값을 변경하여 새 개체를 가리키도록 하면 호출자가 참조하는 저장 위치도 변경됩니다. 다음 예제에서는 참조 형식 인스턴스를 ref 매개 변수로 전달합니다.

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
using System;
 
public class MyClass
{
 
    class Product
    {
        public Product(string name, int newID)
        {
            ItemName = name;
            ItemID = newID;
        }
 
        public string ItemName { get; set; }
        public int ItemID { get; set; }
    }
 
    private static void ModifyProductsByReference()
    {
        // Declare an instance of Product and display its initial values.
        Product item = new Product("Fasteners"54321);
        System.Console.WriteLine("Original values in Main.  Name: {0}, ID: {1}\n",
            item.ItemName, item.ItemID);
 
        // Pass the product instance to ChangeByReference.
        ChangeByReference(ref item);
        System.Console.WriteLine("Back in Main.  Name: {0}, ID: {1}\n",
            item.ItemName, item.ItemID);
    }
 
    private static void ChangeByReference(ref Product itemRef)
    {
        // Change the address that is stored in the itemRef parameter.   
        itemRef = new Product("Stapler"99999);
 
        // You can change the value of one of the properties of
        // itemRef. The change happens to item in Main as well.
        itemRef.ItemID = 12345;
    }
 
    static void Main(string[] args)
    {
        ModifyProductsByReference();
    }
}
 



result : 

Original values in Main.  Name: Fasteners, ID: 54321

Back in Main.  Name: Stapler, ID: 12345



참조 반환 값

참조 반환 값(또는 ref return)은 메서드가 호출자에게 참조로 반환하는 값입니다. 즉, 호출자가 메서드에서 반환된 값을 수정할 수 있으며 해당 변경 내용이 메서드를 포함하는 개체의 상태에 반영됩니다.

참조 반환 값은 ref 키워드를 사용하여 정의됩니다.

  • 메서드 시그니처에서. 예를 들어 다음 메서드 시그니처는 GetCurrentPrice 메서드가 Decimal 값을 참조로 반환함을 나타냅니다.

public ref decimal GetCurrentValue()


  • 메서드의 return 문에서 반환된 return 토큰과 변수 간에. 예:

return ref DecimalArray[0];


호출자가 개체 상태를 수정하려면 참조 반환 값을 참조 로컬로 명시적으로 정의된 변수에 저장해야 합니다.


참조 로컬

참조 지역 변수는 return ref을 사용하여 반환된 값을 참조하는 데 사용됩니다. 참조 지역 변수를 초기화하고 참조 반환 값에 할당해야 합니다. 참조 로컬 값의 수정 내용은 메서드가 값을 참조로 반환하는 개체 상태에 반영됩니다.

변수 선언 앞, 값을 참조로 반환하는 메서드 호출 직전에 ref 키워드를 사용하여 참조 로컬을 정의합니다.

예를 들어 다음 문은 GetEstimatedValue 메서드에서 반환되는 참조 로컬 값을 정의합니다.


ref decimal estValue = ref Building.GetEstimatedValue();


동일한 방법으로 참조로 값에 액세스할 수 있습니다. 경우에 따라 참조로 값에 액세스하면 비용이 많이 들 수 있는 복사 작업을 피함으로써 성능이 향상됩니다. 예를 들어, 다음 명령문은 값을 참조하는 데 사용되는 참조 로컬 값을 정의하는 방법을 보여줍니다.

ref VeryLargeStruct reflocal = ref veryLargeStruct;

두 예에서 ref 키워드는 두 위치에 모두 사용해야 합니다. 그렇지 않으면 컴파일러 오류 CS8172, "값을 사용하여 참조 형식 변수를 초기화할 수 없습니다."가 생성됩니다.


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;
 
public class MyClass
{
 
    public class Book
    {
        public string Author;
        public string Title;
    }
 
    public class BookCollection
    {
        private Book[] books = { new Book { Title = "Call of the Wild, The", Author = "Jack London" },
                        new Book { Title = "Tale of Two Cities, A", Author = "Charles Dickens" }
                       };
        private Book nobook = null;
 
        public ref Book GetBookByTitle(string title)
        {
            for (int ctr = 0; ctr < books.Length; ctr++)
            {
                if (title == books[ctr].Title)
                    return ref books[ctr];
            }
            return ref nobook;
        }
 
        public void ListBooks()
        {
            foreach (var book in books)
            {
                Console.WriteLine($"{book.Title}, by {book.Author}");
            }
            Console.WriteLine();
        }
    }
 
    static void Main(string[] args)
    {
        var bc = new BookCollection();
        bc.ListBooks();
 
        ref Book book = ref  bc.GetBookByTitle("Call of the Wild, The");
        //Book book = bc.GetBookByTitle("Call of the Wild, The");
        if (book != null)
            book = new Book { Title = "Republic, The", Author = "Plato" };
        bc.ListBooks();
    }
}
 


호출자가 GetBookByTitle 메서드에서 참조 로컬로 반환된 값을 저장하는 경우 호출자가 반환 값을 변경하면 다음 예제와 같이 BookCollection 개체에 변경 내용이 반영됩니다.


위의 예제 결과는 

Call of the Wild, The, by Jack London

Tale of Two Cities, A, by Charles Dickens


Republic, The, by Plato

Tale of Two Cities, A, by Charles Dickens


이긴 하지만  2017 community 버전에 default 에선 아래 주석 을 지운다고하여 에러가 나진않는다(기존 위에 한줄은 주석처리)

//ref Book book = ref  bc.GetBookByTitle("Call of the Wild, The");
Book book = bc.GetBookByTitle("Call of the Wild, The");

대신 결과는 값 형식 복사가 일어나게 됨으로 다음처럼 나타나게 된다(컴파일 에러가 나진 않음)

Call of the Wild, The, by Jack London

Tale of Two Cities, A, by Charles Dickens


Call of the Wild, The, by Jack London

Tale of Two Cities, A, by Charles Dickens






가변인자 params


params 키워드를 사용하면 가변 개수의 인수를 사용하는 메서드 매개 변수를 지정할 수 있습니다.

매개 변수 선언이나 지정된 형식의 인수 배열에 지정된 형식의 쉼표로 구분된 인수 목록을 보낼 수 있습니다. 인수를 보내지 않을 수도 있습니다. 인수를 보내지 않는 경우 params 목록의 길이는 0입니다.

메서드 선언에서 params 키워드 뒤에는 추가 매개 변수가 허용되지 않으며, params 키워드 하나만 메서드 선언에 사용할 수 있습니다.




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
94
using System;
 
public class MyClass
{
    public static void UseParams(params int[] list)
    {
        for (int i = 0; i < list.Length; i++)
        {
            Console.Write(list[i] + " ");
        }
        Console.WriteLine();
    }
 
    public static void UseParams2(params object[] list)
    {
        Console.WriteLine(list.GetType() );
        if(list.Length >= 1)
        {
            Console.WriteLine(list[0].GetType());
 
 
            var test = list[0as int[];
            if(test!=null)
            {
                Console.WriteLine("\t" + test[0].GetType());
            }
            
        }
        
        for (int i = 0; i < list.Length; i++)
        {
            Console.Write(list[i] + " ");
        }
        Console.WriteLine();
    }
 
 
 
    static void Main()
    {
        
 
 
        // You can send a comma-separated list of arguments of the 
        // specified type.
        UseParams(1234);
        UseParams2(1'a'"test");
 
        // A params parameter accepts zero or more arguments.
        // The following calling statement displays only a blank line.
        UseParams2();
 
        // An array argument can be passed, as long as the array
        // type matches the parameter type of the method being called.
        int[] myIntArray = { 56789 };
        UseParams(myIntArray);
        Console.WriteLine(typeof(int[]));
        
 
        object[] myObjArray = { 2'b'"test""again" };
        UseParams2(myObjArray);
 
        Console.WriteLine(typeof(object[]));
 
        // The following call causes a compiler error because the object
        // array cannot be converted into an integer array.
        //UseParams(myObjArray);
 
        // The following call does not cause an error, but the entire 
        // integer array becomes the first element of the params array.
        // int[] 전체가 object 가 되기 때문에 에러가 나지 않는다
        UseParams2(myIntArray);
    }
}
/*
Output:
1 2 3 4
System.Object[]
System.Int32
1 a test
System.Object[]
5 6 7 8 9
System.Int32[]
System.Object[]
System.Int32
2 b test again
System.Object[]
System.Object[]
System.Int32[]
        System.Int32
*/
 

.







out


out 키워드를 사용하면 참조를 통해 인수를 전달할 수 있습니다. 이러한 방식은 ref 키워드와 비슷합니다. 단, ref의 경우에는 변수를 전달하기 전에 초기화해야 합니다. in이 호출된 메서드에서 인수 값 수정을 허용하지 않는 것을 제외하고 in 키워드와도 같습니다. out 매개 변수를 사용하려면 메서드 정의와 호출 메서드가 모두 명시적으로 out 키워드를 사용해야 합니다. 예:



int initializeInMethod; OutArgExample(out initializeInMethod); Console.WriteLine(initializeInMethod); // value is now 44 void OutArgExample(out int number) { number = 44; }


out 인수로 전달되는 변수는 메서드 호출에서 전달되기 전에 초기화할 필요가 없지만 호출된 메서드는 메서드가 반환되기 전에 값을 할당해야 합니다.


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
using System;
 
public class MyClass
{
 
    public void SampleMethod(out int i)
    {
        //아래 주석을 풀면 에러 안남
        //오류 CS0177  제어가 현재 메서드를 벗어나기 전에 'i' out 매개 변수를 할당해야 합니다.
        //i = 3;
    }
 
    //이 전체 주석을 풀면 컴파일 에러
    //'MyClass'은(는) 매개 변수 한정자 'ref' 및 'out'만 다른 오버로드된 메서드을(를) 정의할 수 없습니다.
    /*
    public void SampleMethod(ref int i)
    {
    }*/
    static void Main()
    {
 
 
    }
}
 





out 인수 선언

메서드가 여러 값을 반환하도록 하려는 경우 out 인수를 사용하여 메서드를 선언하면 유용합니다. 다음 예제에서는 out을 사용하여 단일 메서드 호출로 3개 변수를 반환합니다. 세 번째 인수는 null에 할당됩니다. 따라서 메서드가 값을 선택적으로 반환할 수 있습니다.


void Method(out int answer, out string message, out string stillNull) { answer = 44; message = "I've been returned"; stillNull = null; } int argNumber; string argMessage, argDefault; Method(out argNumber, out argMessage, out argDefault); Console.WriteLine(argNumber); Console.WriteLine(argMessage); Console.WriteLine(argDefault == null);




C# 7.0부터 별도 변수 선언이 아니라 메서드 호출의 인수 목록에서 out 변수를 선언할 수 있습니다. 이렇게 하면 보다 간결하고 읽기 쉬운 코드가 생성되며 메서드 호출 전에 실수로 변수에 값이 할당되는 경우를 방지할 수 있습니다. 다음 예제는 Int32.TryParse 메서드 호출에서 number 변수를 정의한다는 점을 제외하고 이전 예제와 비슷합니다.


string numberAsString = "1640"; if (Int32.TryParse(numberAsString, out int number)) Console.WriteLine($"Converted '{numberAsString}' to {number}"); else Console.WriteLine($"Unable to convert '{numberAsString}'"); // The example displays the following output: // Converted '1640' to 1640



앞의 예제에서 number 변수는 int로 강력하게 형식화됩니다. 다음 예제와 같이 암시적 형식 지역 변수를 선언할 수도 있습니다.


string numberAsString = "1640"; if (Int32.TryParse(numberAsString, out var number)) Console.WriteLine($"Converted '{numberAsString}' to {number}"); else Console.WriteLine($"Unable to convert '{numberAsString}'"); // The example displays the following output: // Converted '1640' to 1640







(호출된 메서드에서 인수를 수정하지 못하는) in




in 키워드를 사용하면 참조를 통해 인수를 전달할 수 있습니다. 이 키워드는 호출된 메서드에서 in 인수를 수정할 수 없다는 점을 제외하고 ref 또는 out 키워드와 유사합니다. ref 인수는 수정할 수 있지만 out 인수는 호출자가 수정해야 하며, 해당 수정 사항은 호출 컨텍스트에서 식별 가능합니다.


int readonlyArgument = 44; InArgExample(readonlyArgument); Console.WriteLine(readonlyArgument); // value is still 44 void InArgExample(in int number) { // Uncomment the following line to see error CS8331 //number = 19; }



in 인수로 전달되는 변수는 메서드 호출에서 전달되기 전에 초기화되어야 합니다. 그러나 호출된 메서드는 값을 할당하거나 인수를 수정하지 않을 수 있습니다.

inref 및 out 키워드는 서로 다른 런타임 동작을 수행하지만 컴파일 시간에 메서드 시그니처의 일부로 간주되지는 않습니다. 따라서 메서드 하나는 ref 또는 in 인수를 사용하고 다른 하나는 out 인수를 사용한다는 것 외에는 차이점이 없으면 메서드를 오버로드할 수 없습니다. 예를 들어 다음 코드는 컴파일되지 않습니다.


class CS0663_Example { // Compiler error CS0663: "Cannot define overloaded // methods that differ only on in, ref and out". public void SampleMethod(in int i) { } public void SampleMethod(ref int i) { } }


in의 존재에 기반한 오버로딩이 허용됩니다.

class InOverloads { public void SampleMethod(in int i) { } public void SampleMethod(int i) { } }



in 매개 변수를 사용하여 메서드를 정의하면 잠재적인 성능 최적화가 이루어집니다. 일부 struct 형식 인수는 크기가 클 수 있으며 긴밀한 루프 또는 중요한 코드 경로에서 메서드를 호출할 때 해당 구조를 복사하는 비용이 중요합니다. 메서드는 호출된 메서드가 인수의 상태를 수정하지 않으므로 in 매개 변수를 선언하여 해당 인수가 참조로 안전하게 전달될 수 있음을 지정합니다. 이러한 인수를 참조로 전달하면 (잠재적으로) 비용이 많이 드는 복사본을 방지할 수 있습니다.





오버로드 해결 규칙

in 인수에 대한 동기를 이해하여 값과 in 인수로 메서드의 오버로드 해결 규칙을 이해할 수 있습니다. in 매개 변수를 사용하여 메서드를 정의하면 잠재적인 성능 최적화가 이루어집니다. 일부 struct 형식 인수는 크기가 클 수 있으며 긴밀한 루프 또는 중요한 코드 경로에서 메서드를 호출할 때 해당 구조를 복사하는 비용이 중요합니다. 메서드는 호출된 메서드가 인수의 상태를 수정하지 않으므로 in 매개 변수를 선언하여 해당 인수가 참조로 안전하게 전달될 수 있음을 지정합니다. 이러한 인수를 참조로 전달하면 (잠재적으로) 비용이 많이 드는 복사본을 방지할 수 있습니다.

호출 사이트의 인수에 in을 지정하는 것은 일반적으로 선택 사항입니다. 값으로 인수를 전달하고 in 한정자를 사용하여 인수를 전달하는 것 사이에는 의미 체계상 차이가 없습니다. 호출 사이트의 in 한정자는 인수 값이 변경될 수 있음을 나타내지 않아도 되므로 선택 사항입니다. 호출 사이트에서 in 한정자를 명시적으로 추가하여 인수가 값이 아닌 참조로 전달되도록 합니다. 명시적으로 in을 사용하는 경우 다음과 같은 두 가지 효과가 있습니다.

먼저 호출 사이트에서 in을 지정하면 컴파일러가 일치하는 in 매개 변수로 정의된 메서드를 선택하게 됩니다. 그렇지 않으면 두 메서드가 in이 있을 때만 다른 경우 by 값 오버로드가 더 적합합니다.

둘째, in을 지정하면 참조로 인수를 전달할 의사가 있음을 선언하는 것입니다. in에 사용된 인수는 직접 참조할 수 있는 위치를 나타내야 합니다. out 및 ref 인수에는 동일한 일반 규칙이 적용됩니다. 상수, 일반 속성 또는 값을 생성하는 다른 식은 사용할 수 없습니다.그렇지 않으면 호출 사이트에서 in을 생략할 경우 메서드에 대한 읽기 전용 참조로 전달할 임시 변수를 만들 수 있도록 컴파일러에 알립니다. 컴파일러는 in 인수를 사용하여 몇 가지 제한 사항을 해결하기 위해 임시 변수를 만듭니다.

  • 임시 변수는 컴파일 시간 상수를 in 매개 변수로 허용합니다.
  • 임시 변수는 속성 또는 in 매개 변수에 대한 다른 식을 허용합니다.
  • 임시 변수는 인수 형식에서 매개 변수 형식으로의 암시적 변환이 있는 경우 인수를 허용합니다.

앞의 모든 인스턴스에서 컴파일러는 상수, 속성 또는 다른 식의 값을 저장하는 임시 변수를 만듭니다.

다음 코드에서는 이러한 규칙을 보여줍니다.


static void Method(in int argument) { // implementation removed } Method(5); // OK, temporary variable created. Method(5L); // CS1503: no implicit conversion from long to int short s = 0; Method(s); // OK, temporary int created with the value 0 Method(in s); // CS1503: cannot convert from in short to in int int i = 42; Method(i); // passed by readonly reference Method(in i); // passed by readonly reference, explicitly using `in`




이제 by 값 인수를 사용하는 다른 메서드를 사용할 수 있다고 가정하겠습니다. 결과는 다음 코드와 같이 변경됩니다.



static void Method(int argument) { // implementation removed } static void Method(in int argument) { // implementation removed } Method(5); // Calls overload passed by value Method(5L); // CS1503: no implicit conversion from long to int short s = 0; Method(s); // Calls overload passed by value. Method(in s); // CS1503: cannot convert from in short to in int int i = 42; Method(i); // Calls overload passed by value Method(in i); // passed by readonly reference, explicitly using `in`









ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/params

ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/out-parameter-modifier

ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/in-parameter-modifier

ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/ref



이미지컷



반응형
반응형


[GetUpperBound, GetLowerBound]




구문 소개에 앞서 배열 차원 인덱스 반환 함수 설명 (차원 숫자가 클 수록 더 안쪽에 있는 차원을 말한다)

public int GetUpperBound(
	int dimension
)

GetUpperBound(0)배열의 첫 번째 차원에서 마지막 인덱스를 반환 하 고 GetUpperBound(Rank - 1) 마지막 배열의 마지막 차원 인덱스를 반환 합니다.






Copy(Array, Array, Int32)  : 인자들은 첫번째 배열, 두번째 인자, 마지막 숫자는 인덱스 위치까지 복사될 index

System_CAPS_pubmethodSystem_CAPS_staticCopy(Array, Array, Int32)

Array의 요소 범위를 첫 번째 요소부터 복사하여 다른 Array에 첫 번째 요소부터 붙여넣습니다. 길이가 32비트 정수로 지정되어 있습니다.

System_CAPS_pubmethodSystem_CAPS_staticCopy(Array, Array, Int64)

Array의 요소 범위를 첫 번째 요소부터 복사하여 다른 Array에 첫 번째 요소부터 붙여넣습니다. 길이가 64비트 정수로 지정되어 있습니다.

System_CAPS_pubmethodSystem_CAPS_staticCopy(Array, Int32, Array, Int32, Int32)

Array의 요소 범위를 지정한 소스 인덱스부터 복사하여 지정된 대상 인덱스부터 시작하는 다른 Array에 붙여 넣습니다. 길이와 인덱스가 32비트 정수로 지정되어 있습니다.

System_CAPS_pubmethodSystem_CAPS_staticCopy(Array, Int64, Array, Int64, Int64)

Array의 요소 범위를 지정한 소스 인덱스부터 복사하여 지정된 대상 인덱스부터 시작하는 다른 Array에 붙여 넣습니다. 길이와 인덱스가 64비트 정수로 지정되어 있습니다.

System_CAPS_pubmethodCopyTo(Array, Int32)

현재 1차원 배열의 모든 요소를 지정된 대상 배열 인덱스부터 시작하여 지정된 1차원 배열에 복사합니다. 인덱스가 32비트 정수로 지정되어 있습니다.

System_CAPS_pubmethodCopyTo(Array, Int64)

현재 1차원 배열의 모든 요소를 지정된 대상 배열 인덱스부터 시작하여 지정된 1차원 배열에 복사합니다. 인덱스가 64비트 정수로 지정되어 있습니다.



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
using System;
public class SamplesArray
{
 
    public static void Main()
    {
 
        // Creates and initializes a new integer array and a new Object array.
        int[] myIntArray = new int[5] { 12345 };
        Object[] myObjArray = new Object[5] { 2627282930 };
 
        // Prints the initial values of both arrays.
        Console.WriteLine("Initially,");
        Console.Write("integer array:");
        PrintValues(myIntArray);
        Console.Write("Object array: ");
        PrintValues(myObjArray);
 
        // Copies the first two elements from the integer array to the Object array.
        System.Array.Copy(myIntArray, myObjArray, 2); //처음 두개만 복사
 
        // Prints the values of the modified arrays.
        Console.WriteLine("\nAfter copying the first two elements of the integer array to the Object array,");
        Console.Write("integer array:");
        PrintValues(myIntArray);
        Console.Write("Object array: ");
        PrintValues(myObjArray);
 
        // Copies the last two elements from the Object array to the integer array.
//마지막 두개 복사
        System.Array.Copy(myObjArray, myObjArray.GetUpperBound(0- 1, myIntArray, myIntArray.GetUpperBound(0- 12);
 
        // Prints the values of the modified arrays.
        Console.WriteLine("\nAfter copying the last two elements of the Object array to the integer array,");
        Console.Write("integer array:");
        PrintValues(myIntArray);
        Console.Write("Object array: ");
        PrintValues(myObjArray);
    }
 
 
    public static void PrintValues(Object[] myArr)
    {
        foreach (Object i in myArr)
        {
            Console.Write("\t{0}", i);
        }
        Console.WriteLine();
    }
 
    public static void PrintValues(int[] myArr)
    {
        foreach (int i in myArr)
        {
            Console.Write("\t{0}", i);
        }
        Console.WriteLine();
    }
}
/* 
This code produces the following output.
Initially,
integer array:  1       2       3       4       5
Object array:   26      27      28      29      30
After copying the first two elements of the integer array to the Object array,
integer array:  1       2       3       4       5
Object array:   1       2       28      29      30
After copying the last two elements of the Object array to the integer array,
integer array:  1       2       3       29      30
Object array:   1       2       28      29      30
*/
 

cs



ref : https://msdn.microsoft.com/ko-kr/library/system.array(v=vs.110).aspx

반응형
반응형

배열에는 최대 32 차원 초과 있을 수 있습니다.


클래스와 달리는 System.Collections 네임 스페이스, Array 용량이 고정 합니다. 용량을 늘리려면 새 만들어야 Array 필요한 용량으로 개체, 이전에서 요소를 복사 Array 새 레코드로 개체를 삭제 하면 이전 Array합니다.

기본적으로의 최대 크기는 Array 은 2gb (기가바이트)입니다. 64 비트 환경에서 크기 제한을 설정 하 여 방지할 수 있습니다는 enabled 특성에는gcAllowVeryLargeObjects 구성 요소를 true 런타임 환경에서 합니다. 그러나 배열의 지정 된 차원 
(바이트 배열 및 단일 바이트 구조의 배열에 대 한 0X7FFFFFC7) 에 0X7FEFFFFF의 최대 인덱스는 총 4 십억 요소로 제한 수 있습니다.



배열에 둘 이상의 차원이 있을 수 있습니다. 예를 들어 다음 선언은 행 4개, 열 2개의 2차원 배열을 만듭니다.

int[,] array = new int[4, 2];

다음 선언은 세 가지 차원 4, 2, 3의 배열을 만듭니다.


int[, ,] array1 = new int[4, 2, 3];

배열 초기화

다음 예제와 같이 선언 시 배열을 초기화할 수 있습니다.


// 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 } } };

// Accessing array elements.
System.Console.WriteLine(array2D[0, 0]);
System.Console.WriteLine(array2D[0, 1]);
System.Console.WriteLine(array2D[1, 0]);
System.Console.WriteLine(array2D[1, 1]);
System.Console.WriteLine(array2D[3, 0]);
System.Console.WriteLine(array2Db[1, 0]);
System.Console.WriteLine(array3Da[1, 0, 1]);
System.Console.WriteLine(array3D[1, 1, 2]);

// Getting the total count of elements or the length of a given dimension.
var allLength = array3D.Length;
var total = 1;
for (int i = 0; i < array3D.Rank; i++) {
    total *= array3D.GetLength(i);
}
System.Console.WriteLine("{0} equals {1}", allLength, total);

// Output:
// 1
// 2
// 3
// 4
// 7
// three
// 8
// 12
// 12 equals 12


차수를 지정하지 않고 배열을 초기화할 수도 있습니다.


int[,] array4 = { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };

초기화하지 않고 배열 변수를 선언하도록 선택할 경우 new 연산자를 사용하여 변수에 배열을 할당해야 합니다. new 사용은 다음 예제에서 확인할 수 있습니다.


int[,] array5;
array5 = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 } };   // OK
//array5 = {{1,2}, {3,4}, {5,6}, {7,8}};   // Error


다음 예제에서는 특정 배열 요소에 값을 할당합니다.


array5[2, 1] = 25;

마찬가지로, 다음 예제에서는 특정 배열 요소의 값을 가져와 elementValue 변수에 할당합니다.


int elementValue = array5[2, 1];

다음 코드 예제에서는 가변 배열을 제외하고 배열 요소를 기본 값으로 초기화합니다.


int[,] array6 = new int[10, 10];







구문 소개에 앞서 배열 차원 인덱스 반환 함수 설명 (차원 숫자가 클 수록 더 안쪽에 있는 차원을 말한다)

public int GetUpperBound(
	int dimension
)

GetUpperBound(0)배열의 첫 번째 차원에서 마지막 인덱스를 반환 하 고 GetUpperBound(Rank - 1) 마지막 배열의 마지막 차원 인덱스를 반환 합니다.





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
using System;
using static System.Console;
 
namespace intel
{
    public class test1
    {
        public static void Main(string[] args)
        {
 
            // Create a one-dimensional integer array.
            int[] integers = { 2468101214161820 };
            // Get the upper and lower bound of the array.
            int upper = integers.GetUpperBound(0);
            int lower = integers.GetLowerBound(0);
            Console.WriteLine("Elements from index {0} to {1}:", lower, upper);
            // Iterate the array.
            for (int ctr = lower; ctr <= upper; ctr++)
                Console.Write("{0}{1}{2}", ctr == lower ? "   " : "",
                                          integers[ctr],
                                          ctr < upper ? ", " : Environment.NewLine);
 
            Console.WriteLine();
 
 
            // Create a two-dimensional integer array.
            int[,] integers2d = { 
                {24}, 
                {39}, 
                {416}, 
                {525}, 
                {636}, 
                {749}, 
                {864}, 
                {981} };
            
            // Get the number of dimensions.                               
            int rank = integers2d.Rank;
            Console.WriteLine("Number of dimensions: {0}", rank);
            for (int ctr = 0; ctr < integers2d.Rank - 1; ctr++)
            {
                Console.WriteLine("   Dimension {0}: from {1} to {2}", ctr, integers2d.GetLowerBound(ctr), integers2d.GetUpperBound(ctr));
            }
                
 
 
            // Iterate the 2-dimensional array and display its values.
            Console.WriteLine("   Values of array elements:");
            for (int outer = integers2d.GetLowerBound(0); outer <= integers2d.GetUpperBound(0); outer++)
            {
                for (int inner = integers2d.GetLowerBound(1); inner <= integers2d.GetUpperBound(1); inner++)
                {
                    Console.Write("      {3}{0}, {1}{4} = {2}", outer, inner, integers2d.GetValue(outer, inner), "{""}");
                }
                Console.WriteLine("");
            }
 
        }
 
    }
}
 
 

cs




결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Elements from index 0 to 9:
   2, 4, 6, 8, 10, 12, 14, 16, 18, 20
 
Number of dimensions: 2
   Dimension 0: from 0 to 7
   Values of array elements:
      {0, 0} = 2      {0, 1} = 4
      {1, 0} = 3      {1, 1} = 9
      {2, 0} = 4      {2, 1} = 16
      {3, 0} = 5      {3, 1} = 25
      {4, 0} = 6      {4, 1} = 36
      {5, 0} = 7      {5, 1} = 49
      {6, 0} = 8      {6, 1} = 64
      {7, 0} = 9      {7, 1} = 81
cs







ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/arrays/multidimensional-arrays

ref : https://msdn.microsoft.com/ko-kr/library/system.array(v=vs.110).aspx

ref : https://msdn.microsoft.com/ko-kr/library/system.array.getupperbound(v=vs.110).aspx

반응형
반응형

Nullable 형식 사용




nullable 형식은 기본 형식의 모든 값과 추가 null 값을 나타낼 수 있습니다. nullable 형식은 다음과 같은 두 가지 방법 중 하나로 선언됩니다.

System.Nullable<T> variable

또는

T? variable

T는 nullable 형식의 기본 형식입니다. T는 struct를 비롯한 모든 값 형식일 수 있으나, 참조 형식일 수는 없습니다.


object? sdf;  //컴파일 오류


nullable 형식을 사용할 수 있는 경우의 예를 들기 위해 일반 부울 변수가 두 가지 값 true와 false를 가질 수 있는 방법을 고려합니다. “정의되지 않음”을 의미하는 값은 없습니다. 많은 프로그래밍 응용 프로그램(특히, 데이터베이스 상호 작용)에서 변수는 정의되지 않은 상태로 발생할 수 있습니다. 예를 들어 데이터베이스의 필드는 true 또는 false 값을 포함할 수 있지만, 전혀 값을 포함하지 않을 수도 있습니다. 마찬가지로, 초기화되지 않았음을 나타내기 위해 참조 형식을 null로 설정할 수 있습니다.

이 차이로 인해 상태 정보, 특수 값 사용 등을 저장하는 데 사용되는 추가 변수와 관련한 추가 프로그래밍 작업이 발생할 수 있습니다. C#에서는 nullable 형식 한정자를 사용하여 정의되지 않은 값을 나타내는 값 형식 변수를 만들 수 있습니다.



nullable 형식의 예

nullable 형식에 대한 기준으로 모든 값 형식을 사용할 수 있습니다. 예:


1
2
3
4
5
int? i = 10;
double? d1 = 3.14;
bool? flag = null;
char? letter = 'a';
int?[] arr = new int?[10];
cs

nullable 형식의 멤버

nullable 형식의 각 인스턴스에는 다음과 같은 두 개의 public 읽기 전용 속성이 포함됩니다.

  • HasValue

    HasValue는 bool 형식입니다. 변수가 null이 아닌 값을 포함한 경우 true로 설정됩니다.

  • Value

    Value는 기본 형식과 같은 형식입니다. HasValue가 true이면 Value에는 의미 있는 값이 포함됩니다. HasValue가 false이면 Value에서는 InvalidOperationException을 throw합니다.

이 예제에서 HasValue 멤버는 변수를 표시하려고 하기 전에 변수가 값을 포함하는지를 테스트하는 데 사용됩니다.

1
2
3
4
5
6
7
8
9
10
int? x = 10;
if (x.HasValue)
{
    System.Console.WriteLine(x.Value);
}
else
{
    System.Console.WriteLine("Undefined");
}
 

cs


값에 대한 테스트는 다음 예제처럼 수행할 수도 있습니다.


1
2
3
4
5
6
7
8
9
int? y = 10;
if (y != null)
{
    System.Console.WriteLine(y.Value);
}
else
{
    System.Console.WriteLine("Undefined");
}



명시적 변환

nullable 형식은 캐스트를 사용하여 명시적으로 또는 Value 속성을 사용하여 일반 형식으로 캐스팅할 수 있습니다. 예:


1
2
3
4
5
int? n = null;
 
//int m1 = n;      // Will not compile.
int m2 = (int)n;   // Compiles, but will create an exception if n is null.
int m3 = n.Value;  // Compiles, but will create an exception if n is null.


두 데이터 형식 간에 사용자 정의 변환이 정의된 경우 이러한 데이터 형식의 nullable 버전에도 같은 변환을 사용할 수 있습니다.

암시적 변환

nullable 형식의 변수는 다음 예제와 같이 null 키워드를 사용하여 null로 설정할 수 있습니다.

int? n1 = null;


일반 형식에서 nullable 형식으로의 변환은 암시적입니다.

int? n2; n2 = 10; // Implicit conversion.


연산자

미리 정의된 단항 및 이항 연산자와 값 형식에 대해 존재하는 모든 사용자 정의 연산자는 nullable 형식에도 사용할 수 있습니다. 이러한 연산자는 피연산자가 null인 경우 null 값을 생성하고, 그렇지 않으면 연산자는 포함된 값을 사용하여 결과를 계산합니다. 예:

1
2
3
4
5
6
int? a = 10;
int? b = null;
 
a++;         // Increment by 1, now a is 11.
= a * 10;  // Multiply by 10, now a is 110.
= a + b;   // Add b, now a is null.
cs

nullable 형식과의 비교를 수행할 때 nullable 형식 중 하나의 값이 null이고 나머지는 null이 아닌 경우 모든 비교는 !=(같지 않음)을 제외하고는 false로 계산됩니다. 특정 비교에서는 false를 반환하고 그 반대의 경우에는 true를 반환한다고 가정하지 않는 것이 중요합니다. 다음 예제에서 10은 null보다 크지 않고, 작지 않고, 같지도 않습니다. num1 != num2만 true로 계산됩니다.


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
int? num1 = 10;
int? num2 = null;
if (num1 >= num2)
{
    Console.WriteLine("num1 is greater than or equal to num2");
}
else
{
    // This clause is selected, but num1 is not less than num2.
    Console.WriteLine("num1 >= num2 returned false (but num1 < num2 also is false)");
}
 
if (num1 < num2)
{
    Console.WriteLine("num1 is less than num2");
}
else
{
    // The else clause is selected again, but num1 is not greater than
    // or equal to num2.
    Console.WriteLine("num1 < num2 returned false (but num1 >= num2 also is false)");
}
 
if (num1 != num2)
{
    // This comparison is true, num1 and num2 are not equal.
    Console.WriteLine("Finally, num1 != num2 returns true!");
}
 
// Change the value of num1, so that both num1 and num2 are null.
num1 = null;
if (num1 == num2)
{
    // The equality comparison returns true when both operands are null.
    Console.WriteLine("num1 == num2 returns true when the value of each is null");
}
 
/* Output:
 * num1 >= num2 returned false (but num1 < num2 also is false)
 * num1 < num2 returned false (but num1 >= num2 also is false)
 * Finally, num1 != num2 returns true!
 * num1 == num2 returns true when the value of each is null
 */



둘 다 null인 두 nullable 형식의 같음 비교는 true로 계산됩니다.

?? 연산자

?? 연산자는 nullable 형식이 nullable 형식이 아닌 형식에 할당된 경우 반환되는 기본값을 정의합니다.

말이 좀 이상한데 쉽게 말한다면 c가 null 인경우 -1 을 ? 아닌 변수 d 에 할당한다(초기화 한다)로 보면 됩니다



int? c = null;

// d = c, unless c is null, in which case d = -1.
int d = c ?? -1;

이 연산자는 여러 nullable 형식에도 사용할 수 있습니다. 예:


아래 같은 경우에는 e 와 f 가 모두 null 임으로 -1 이 g 에 할당됩니다


int? e = null;
int? f = null;

// g = e or f, unless e and f are both null, in which case g = -1.
int g = e ?? f ?? -1;


bool? 형식

bool? nullable 형식은 세 가지 값 truefalse 및 null을 포함할 수 있습니다. 
bool?에서 bool로 캐스팅하는 방법에 대한 자세한 내용은 방법: bool?에서 bool로 안전하게 캐스팅을 참조하세요.

nullable 부울은 SQL에서 사용되는 부울 변수 형식과 유사합니다. & 및 | 연산자에 의해 생성된 결과가 SQL의 삼중값 부울 형식과 일치하도록 다음과 같은 미리 정의된 연산자가 제공됩니다.

bool? operator &(bool? x, bool? y)

bool? operator |(bool? x, bool? y)

다음 표는 이러한 연산자의 결과를 보여 줍니다.

Xyx&yx|y
truetruetruetrue
trueFalsefalsetrue
truenullnulltrue
FalsetrueFalsetrue
FalseFalseFalseFalse
FalsenullFalsenull
nulltruenulltrue
nullFalseFalsenull
nullnullnullnull




ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/nullable-types/using-nullable-types





반응형
반응형
실수형 decimal (128bit) 


이름이 decimal 인데 실수형이다 


큰 데이터를 다룰때 사용된다





decimal 키워드는 128비트 데이터 형식을 나타냅니다. decimal 형식은 부동 소수점 형식보다 전체 자릿수는 크고 범위는 작아서 재무 및 통화 계산에 적합합니다. 다음 표에서는 decimal 형식의 대략적인 범위와 전체 자릿수를 보여 줍니다.

형식근사 범위전체 자릿수.NET 형식
decimal(-7.9 x 1028 ~ 7.9 x 1028) / (100 ~ 1028)28-29개의 유효 자릿수System.Decimal



decimal의 기본값은 0m입니다.

리터럴

숫자 형식의 실수 리터럴이 decimal로 처리되게 하려면 다음과 같이 접미사 m 또는 M을 사용합니다.

C#
decimal myMoney = 300.5m;

m 접미사가 없으면 숫자가 double로 처리되어 컴파일러 오류가 발생합니다.

변환

정수 형식은 암시적으로 decimal로 변환되어 계산 결과가 decimal로 나타납니다. 따라서 접미사를 붙이지 않고 정수 리터럴을 사용하여 decimal 변수를 초기화할 수 있습니다. 예를 들면 다음과 같습니다.

C#
decimal myMoney = 300;

다른 부동 소수점 형식과 decimal 형식 간의 암시적 변환은 없습니다. 따라서 이 두 형식 간의 변환에는 캐스트를 사용해야 합니다. 예:

C#
decimal myMoney = 99.9m;
double x = (double)myMoney;
myMoney = (decimal)x;

또한 같은 식에서 decimal과 숫자 정수 형식을 혼합할 수 있습니다. 그러나 캐스트를 사용하지 않고 decimal과 다른 부동 소수점 형식을 혼합하면 컴파일 오류가 발생합니다.

암시적 숫자 변환에 대한 자세한 내용은 암시적 숫자 변환 표를 참조하세요.

명시적 숫자 변환에 대한 자세한 내용은 명시적 숫자 변환 표를 참조하세요.

decimal 출력 서식 지정

String.Format 메서드를 사용하거나 Console.Write을 호출하는 String.Format() 메서드를 통해 결과의 서식을 지정할 수 있습니다. 통화 서식은 이 문서 뒷부분에 있는 두 번째 예제처럼 표준 통화 서식 문자열 "C" 또는 "c"를 사용하여 지정합니다. String.Format 메서드에 대한 자세한 내용은 String.Format을 참조하십시오.

이 예제에서는 통화 서식 문자열을 사용하여 출력 서식을 지정합니다. x는 소수 자릿수가 $0.99를 초과하기 때문에 반올림됩니다. (통화 서식 때문에 1.00 으로 처리됨 => :C 를 제거 하면 소수부가 모두 표시됩니다)
최대 자릿수를 나타내는 변수 y는 올바른 서식으로 정확하게 표시됩니다.

C#
public class TestDecimalFormat
{
    static void Main()
    {
        decimal x = 0.999m;
        decimal y = 9999999999999999999999999999m;
        Console.WriteLine("My amount = {0:C}", x);
        Console.WriteLine("Your amount = {0:C}", y);
    }
}
/* Output:
    My amount = $1.00
    Your amount = $9,999,999,999,999,999,999,999,999,999.00
*/



ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/decimal



반응형
반응형

object(C# 참조)


object 형식은 .NET Framework에서 Object의 별칭입니다. 

C#의 통합 형식 시스템에서 사용자 정의 및 미리 정의된 참조 형식과 값 형식을 비롯한 모든 형식은 

직접 또는 간접적으로 Object에서 상속합니다. object 형식의 변수에 모든 형식의 값을 할당할 수 있습니다. 

값 형식의 변수가 개체로 변환된 경우 boxed라고 합니다. 

형식 개체의 변수가 값 형식으로 변환된 경우 unboxed라고 합니다. 

자세한 내용은 boxing 및 unboxing을 참조하세요.

다음 샘플은 object 형식의 변수가 모든 데이터 형식의 값을 허용할 수 있는 방법 및 object 형식의 변수가 .NET Framework의 Object에 대해 메서드를 사용할 수 있는 방법을 보여 줍니다.




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
class ObjectTest
{
   public int i = 10;
}
 
class MainClass2
{
   static void Main()
   {
      object a;
      a = 1;   // an example of boxing
      Console.WriteLine(a);
      Console.WriteLine(a.GetType());
      Console.WriteLine(a.ToString());
 
      a = new ObjectTest();
      ObjectTest classRef;
      classRef = (ObjectTest)a;
      Console.WriteLine(classRef.i);
   }
}
/* Output
    1
    System.Int32
    1
 * 10
*/



반응형
반응형


String.Format Placeholder/서식


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
94
95
96
97
98
99
100
101
102
 
Decimal pricePerOunce = 17.36m;
String s = String.Format("The current price is {0} per ounce.",
                         pricePerOunce);
// Result: The current price is 17.36 per ounce.
String s = String.Format("The current price is {0:C2} per ounce.",
                         pricePerOunce);
// Result if current culture is en-US:
//      The current price is $17.36 per ounce.
 
 
 
 
//String.Format형식 문자열 뒤에 하나 이상의 개체 또는 문자열로 변환 되며 형식 문자열에서 지정된 된 위치에 삽입 하는 
//식으로 시작 합니다. 예:
decimal temp = 20.4m;
string s = String.Format("The temperature is {0}°C.", temp);
Console.WriteLine(s);
// Displays 'The temperature is 20.4°C.'
 
//{0} 형식에서 문자열은 형식 항목입니다. 0문자열 값을 해당 위치에 삽입할 개체의 인덱스가입니다. (인덱스 0부터 시작)입니다. 
//삽입 될 개체가 문자열이 아닌 경우 해당 ToString 메서드를 호출 하는 결과 문자열에 삽입 하기 전에 하나를 변환 합니다.
 
 
 
 
string s = String.Format("At {0}, the temperature is {1}°C.",
                         DateTime.Now, 20.4);
// Output similar to: 'At 4/10/2015 9:29:41 AM, the temperature is 20.4°C.'
 
 
 
 
//서식 지정 제어
//개체의 서식 지정 하는 방법을 제어 하는 형식 문자열 형식 항목의 인덱스를 따를 수 있습니다. 
//예를 들어 {0:d} 개체 목록에서 첫 번째 개체 "d" 형식 문자열에 적용 됩니다. 다음은 단일 개체와 예제 하 고 
//두 항목의 서식을 지정 합니다.
 
string s = String.Format("It is now {0:d} at {0:t}", DateTime.Now);
// Output similar to: 'It is now 4/10/2015 at 10:04 AM'
 
//형식 문자열, 모든 숫자 형식을 포함 한 다양 한 형식 지원 (둘 다 표준 및 사용자 지정 형식 문자열), 모든 날짜 및 
//시간 (둘 다 표준 및 사용자 지정 형식 문자열) 
//및 시간 간격 (둘 다 표준 및 사용자 지정 형식 문자열), 모든 열거형 형식 열거형 형식, 및 GUIDs합니다. 
//또한 사용자 고유의 형식에 형식 문자열에 대 한 지원을 추가할 수 있습니다.
 
 
 
 
 
 
//{0:-15} 또는 //{0:15}  + 
//- 가 오면 글자가 왼쪽부터 배치된다
//+ 가 오면 글자가 오른쪽부터 배치된다
//간격을 제어합니다.
//결과 문자열에 삽입 되는 문자열의 너비를 정의할 수 있습니다 {0,12}, 
//문자열 12를 삽입 합니다. 이 경우 첫 번째 개체의 문자열 표현을 12 자 필드에 오른쪽 맞춤를입니다.
//(첫 번째 개체의 문자열 표현을 길이 12 자 이면 그러나 기본 필드 너비는 무시 됩니다 및 
//전체 문자열이 결과 문자열에 삽입 됩니다.)
 
//다음 예제에서는 정의 문자열을 보관할 6 자 필드 "Year" 및 일부 연도 문자열으로 15 자 필드는 문자열을 보관할 "채우기"와
//일부 인구 데이터입니다. 문자는 오른쪽 정렬 필드에 note 합니다.
 
int[] years = { 201320142015 };
int[] population = { 102563211059671148203 };
String s = String.Format("{0,6} {1,15}\n\n""Year""Population");
for(int index = 0; index < years.Length; index++)
   s += String.Format("{0,6} {1,15:N0}\n",
                      years[index], population[index]);
// Result:
//      Year      Population
//
//      2013       1,025,632
//      2014       1,105,967
//      2015       1,148,203
 
 
 
 
 
 
//맞춤을 제어합니다.
//기본적으로 문자열은 해당 필드 내에서 오른쪽 정렬 필드 너비를 지정 하는 경우입니다. 문자열 필드에서를 
//왼쪽에 맞추려면 앞에 음수 기호를 사용 하 여 필드 너비와 같은 {0,-12} 12 자로 오른쪽 정렬 필드를 정의 합니다.
//다음 예제에서는 왼쪽 맞춤 레이블과 데이터 한다는 점을 제외 하면 이전 쿼리와 비슷합니다.
 
int[] years = { 201320142015 };
int[] population = { 102563211059671148203 };
String s = String.Format("{0,-10} {1,-10}\n\n""Year""Population");
for(int index = 0; index < years.Length; index++)
   s += String.Format("{0,-10} {1,-10:N0}\n",
                      years[index], population[index]);
// Result:
//    Year       Population
//
//    2013       1,025,632
//    2014       1,105,967
//    2015       1,148,203
 
 
 
 
 
 
 




ref : https://msdn.microsoft.com/ko-kr/library/system.string.format(v=vs.110).aspx



반응형
반응형



using static System.Console;


C# 6.0 이후 버전 이후부터 위 구문을 사용하여 WriteLine 명령어를 간단하게 사용 할 수 잇다





다음 예제에서는 형식 이름을 지정할 필요 없이 using static 지시문을 사용하여 ConsoleMath 및 String 클래스의 정적 멤버를 사용 가능하게 합니다.


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
using System;
using static System.Console;
using static System.Math;
using static System.String;
 
class Program
{
   static void Main()
   {
      Write("Enter a circle's radius: ");
      var input = ReadLine();
      if (!IsNullOrEmpty(input) && double.TryParse(input, out var radius)) {
         var c = new Circle(radius);
         
         string s = "\nInformation about the circle:\n";
         s = s + Format("   Radius: {0:N2}\n", c.Radius);
         s = s + Format("   Diameter: {0:N2}\n", c.Diameter);
         s = s + Format("   Circumference: {0:N2}\n", c.Circumference);
         s = s + Format("   Area: {0:N2}\n", c.Area);
         WriteLine(s);
      }
      else {
         WriteLine("Invalid input...");
      }
   }
}
 
public class Circle
{
   public Circle(double radius)
   {
      Radius = radius;
   }
 
   public double Radius { get; set; }
 
   public double Diameter
   {
      get { return 2 * Radius; }
   }
 
   public double Circumference
   {
      get { return 2 * Radius * PI; }      
   }
 
   public double Area
   {
      get { return PI * Pow(Radius, 2); }
   }
}
// The example displays the following output:
//       Enter a circle's radius: 12.45
//       
//       Information about the circle:
//          Radius: 12.45
//          Diameter: 24.90
//          Circumference: 78.23
//          Area: 486.95





ref : https://docs.microsoft.com/ko-kr/dotnet/csharp/language-reference/keywords/using-static



반응형
반응형

int si = 3; Console.WriteLine$"{si}" ); //6.0 버전부터 $"{변수명}" 의 표기법으로 출력 가능하다

// This code example demonstrates the Console.WriteLine() method.
// Formatting for this example uses the "en-US" culture.

using System;
class Sample 
{
    enum Color {Yellow = 1, Blue, Green};
    static DateTime thisDate = DateTime.Now;

    public static void Main() 
    {
    Console.Clear();

// Format a negative integer or floating-point number in various ways.
    Console.WriteLine("Standard Numeric Format Specifiers");
    Console.WriteLine(
        "(C) Currency: . . . . . . . . {0:C}\n" +
        "(D) Decimal:. . . . . . . . . {0:D}\n" +
        "(E) Scientific: . . . . . . . {1:E}\n" +
        "(F) Fixed point:. . . . . . . {1:F}\n" +
        "(G) General:. . . . . . . . . {0:G}\n" +
        "    (default):. . . . . . . . {0} (default = 'G')\n" +
        "(N) Number: . . . . . . . . . {0:N}\n" +
        "(P) Percent:. . . . . . . . . {1:P}\n" +
        "(R) Round-trip: . . . . . . . {1:R}\n" +
        "(X) Hexadecimal:. . . . . . . {0:X}\n",
        -123, -123.45f); 

// Format the current date in various ways.
    Console.WriteLine("Standard DateTime Format Specifiers");
    Console.WriteLine(
        "(d) Short date: . . . . . . . {0:d}\n" +
        "(D) Long date:. . . . . . . . {0:D}\n" +
        "(t) Short time: . . . . . . . {0:t}\n" +
        "(T) Long time:. . . . . . . . {0:T}\n" +
        "(f) Full date/short time: . . {0:f}\n" +
        "(F) Full date/long time:. . . {0:F}\n" +
        "(g) General date/short time:. {0:g}\n" +
        "(G) General date/long time: . {0:G}\n" +
        "    (default):. . . . . . . . {0} (default = 'G')\n" +
        "(M) Month:. . . . . . . . . . {0:M}\n" +
        "(R) RFC1123:. . . . . . . . . {0:R}\n" +
        "(s) Sortable: . . . . . . . . {0:s}\n" +
        "(u) Universal sortable: . . . {0:u} (invariant)\n" +
        "(U) Universal full date/time: {0:U}\n" +
        "(Y) Year: . . . . . . . . . . {0:Y}\n", 
        thisDate);

// Format a Color enumeration value in various ways.
    Console.WriteLine("Standard Enumeration Format Specifiers");
    Console.WriteLine(
        "(G) General:. . . . . . . . . {0:G}\n" +
        "    (default):. . . . . . . . {0} (default = 'G')\n" +
        "(F) Flags:. . . . . . . . . . {0:F} (flags or integer)\n" +
        "(D) Decimal number: . . . . . {0:D}\n" +
        "(X) Hexadecimal:. . . . . . . {0:X}\n", 
        Color.Green);       
    }
}
/*
This code example produces the following results:

Standard Numeric Format Specifiers
(C) Currency: . . . . . . . . ($123.00)
(D) Decimal:. . . . . . . . . -123
(E) Scientific: . . . . . . . -1.234500E+002
(F) Fixed point:. . . . . . . -123.45
(G) General:. . . . . . . . . -123
    (default):. . . . . . . . -123 (default = 'G')
(N) Number: . . . . . . . . . -123.00
(P) Percent:. . . . . . . . . -12,345.00 %
(R) Round-trip: . . . . . . . -123.45
(X) Hexadecimal:. . . . . . . FFFFFF85

Standard DateTime Format Specifiers
(d) Short date: . . . . . . . 6/26/2004
(D) Long date:. . . . . . . . Saturday, June 26, 2004
(t) Short time: . . . . . . . 8:11 PM
(T) Long time:. . . . . . . . 8:11:04 PM
(f) Full date/short time: . . Saturday, June 26, 2004 8:11 PM
(F) Full date/long time:. . . Saturday, June 26, 2004 8:11:04 PM
(g) General date/short time:. 6/26/2004 8:11 PM
(G) General date/long time: . 6/26/2004 8:11:04 PM
    (default):. . . . . . . . 6/26/2004 8:11:04 PM (default = 'G')
(M) Month:. . . . . . . . . . June 26
(R) RFC1123:. . . . . . . . . Sat, 26 Jun 2004 20:11:04 GMT
(s) Sortable: . . . . . . . . 2004-06-26T20:11:04
(u) Universal sortable: . . . 2004-06-26 20:11:04Z (invariant)
(U) Universal full date/time: Sunday, June 27, 2004 3:11:04 AM
(Y) Year: . . . . . . . . . . June, 2004

Standard Enumeration Format Specifiers
(G) General:. . . . . . . . . Green
    (default):. . . . . . . . Green (default = 'G')
(F) Flags:. . . . . . . . . . Green (flags or integer)
(D) Decimal number: . . . . . 3
(X) Hexadecimal:. . . . . . . 00000003

*/


반응형
반응형

자동 완성(인텔리센스)


namespace 탭탭 을 두번 누르면 { } 가 자동 완성된다 

for 탭탭 하면 기본 for 구문이 만들어짐

svm 탭탭  하면

static void Main(string[] args)
    {
 
    }


이 구문이 자동 완성 되며

class 탭탭 의 경우도 

class MyClass
{
 
}

처럼 자동으로 만들어지며 커서거 MyClass 에 가 있게 되어 바로 클래스 명을 수정 할 수 있는 편의 기능을 제공한다 VS 에서..









\r 의 경우 커서를 제잎 앞으로 이동 시키는 명령어가 된다



 
"  " 사이에 개행을 했어도 @ 를 " 앞에 붙이면 여러 줄에 걸처 문자열을 적을 수 있다(단순 편리성 제공)
Console.WriteLine(@"s 
    df");



이스케이프 시퀀스



백슬래시(\) 뒤에 한 문자나 숫자 조합이 오는 문자 조합을 "이스케이프 시퀀스"라고 합니다. 줄 바꿈 문자, 작은따옴표, 또는 문자 상수의 다른 특정 문자를 나타내려면 이스케이프 시퀀스를 사용해야 합니다. 이스케이프 시퀀스는 단일 문자로 간주되므로 문자 상수로 유효합니다.

이스케이프 시퀀스는 일반적으로 터미널과 프린터의 캐리지 리턴 및 탭 이동과 같은 동작을 지정하는 데 사용됩니다. 또한 인쇄할 수 없는 문자 및 큰따옴표(")와 같이 일반적으로 특별한 의미를 가진 문자의 리터럴 표현을 제공하는 데 사용됩니다. 다음 표에서는 ANSI 이스케이프 시퀀스 및 나타내는 사항을 보여 줍니다.

앞에 백슬래시가 오는 물음표(\?)는 문자 시퀀스가 삼중자로 잘못 해석되는 경우 리터럴 물음표를 지정합니다. 자세한 내용은 삼중자를 참조하세요.

이스케이프 시퀀스

이스케이프 시퀀스표현
\a벨(경고)
\b백스페이스
\f폼 피드
\n줄 바꿈
\r캐리지 리턴
\t가로 탭
\v세로 탭
\'작은따옴표
\"큰따옴표
\\백슬래시
\?리터럴 물음표
\ ooo8진수 표기법의 ASCII 문자
\x hh16진수 표기법의 ASCII 문자
\x hhhh이 이스케이프 시퀀스가 와이드 문자 상수 또는 유니코드 문자열 리터럴에 사용되는 경우 16진수 표기법의 유니코드 문자입니다.

예를 들어 WCHAR f = L'\x4e00' 또는 WCHAR b[] = L"The Chinese character for one is \x4e00"로 이름을 지정할 수 있습니다.

Microsoft 전용

표에 없는 문자 앞에 백슬래시가 오는 경우 컴파일러는 정의되지 않은 문자를 문자 자체로 처리합니다. 예를 들어 \c는 c로 처리됩니다.

Microsoft 전용 종료

이스케이프 시퀀스를 사용하여 디스플레이 장치에 비그래픽 제어 문자를 보낼 수 있습니다. 예를 들어 ESC 문자(\033)는 터미널 또는 프린터에 대한 제어 명령의 첫 문자로 자주 사용됩니다. 일부 이스케이프 시퀀스는 장치별로 적용됩니다. 예를 들어 세로 탭 및 용지 공급 이스케이프 시퀀스(\v 및 \f)는 화면 출력에는 영향을 주지 않고 해당 프린터 작업을 수행합니다.

백슬래시(\)를 연속 문자로 사용할 수도 있습니다. 줄 바꿈 문자(Return 키 누름과 같음)가 백슬래시 바로 뒤에 오는 경우 컴파일러는 백슬래시와 줄 바꿈 문자를 무시하고 다음 줄을 앞 줄의 일부로 처리합니다. 한 줄보다 긴 전처리기 정의에 주로 유용합니다. 예:



1
2
#define assert(exp) \  
( (exp) ? (void) 0:_assert( #exp, __FILE__, __LINE__ ) )  



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










반응형
반응형

0. Variadic template

C++11의 emplace 함수에 생긴 큰 변화는 variadic template으로 인해 가능해졌다고 할 수 있다.
Variaidic template 페이지를 충분히 숙지한 이후 아래 내용을 읽어 나가면 훨씬 이해가 쉬울 것이다.


1. C++0x의 emplace 함수

VS2010까지의 emplace 함수들은 단순히 rvalue-reference를 인자로 받아 기존의 추가 함수를 호출하는 형식이었다,

  1. // VS2010의 vector::emplace_back
  2. void emplace_back(_Ty&& _Val)
  3. {      
  4.     // insert element at end
  5.     push_back(_STD forward<_Ty>(_Val));
  6. }
  7.  
  8. template<typename _Valty>
  9. void emplace_back(_Valty&& _Val)
  10. {      
  11.     // insert element at end
  12.     if (this->_Mylast == this->_Myend)
  13.         _Reserve(1);
  14.     _Orphan_range(this->_Mylast, this->_Mylast);
  15.                
  16.     _Cons_val(this->_Alval, this->_Mylast, _STD forward<_Valty>(_Val));
  17.     ++this->_Mylast;
  18. }

즉, C++0x까지의 emplace 계열 함수는 외부에서 생성된 객체를 넘기는 방식이었기에, 
emplace 계열 함수를 쓰더라도, 외부에서 객체 생성 -> 이동 -> 객체 파괴가 이루어졌던 것이다.

물론, 객체 생성 -> 복사 -> 객체 파괴보다는 성능상 이득이 있을 수 있다.
(이동을 어떻게 짰느냐에 따라...)


2. C++11의 emplace 함수 

emplace 함수들이 variadic template으로 인해 진정한 emplace 계열 함수로 거듭나게 되었다.

  1. // VS2013의 vector::emplace_back
  2. template<typename... _Valty>
  3. void emplace_back(_Valty&&... _Val)
  4. {
  5.     // insert by moving into element at end
  6.     if (this->_Mylast == this->_Myend)
  7.         _Reserve(1);
  8.     _Orphan_range(this->_Mylast, this->_Mylast);
  9.  
  10.     this->_Getal().construct(this->_Mylast, _STD forward<_Valty>(_Val)...);            
  11.     ++this->_Mylast;
  12. }

C++11의 emplace 계열 함수는 객체 생성자의 인자들을 넘겨,
컨테이너 내부에서 생성 후 추가하는 방식을 사용하기에, 임시 객체를 아예 생기지 않게 하거나, 그 횟수를 줄일 수 있다.

우선 vector의 emplace_back 함수의 예제를 살펴보자. (cppreference.com의 예제 도용 ㅋ)
아래 예제에서는 임시 객체 생성을 완전히 회피할 수 있다.

  1. #include <vector>
  2. #include <string>
  3. #include <iostream>
  4.  
  5. using namespace std;
  6.  
  7. struct President
  8. {
  9.     string name;
  10.     string country;
  11.     int year;
  12.  
  13.     President(string p_name, string p_country, int p_year)
  14.         : name(move(p_name)), country(move(p_country)), year(p_year)
  15.     {
  16.         cout << "I am being constructed.\n";
  17.     }
  18.  
  19.     President(const President& other)
  20.         : name(move(other.name)), country(move(other.country)), year(other.year)
  21.     {
  22.         cout << "I am being copied.\n";
  23.     }
  24.  
  25.     President(President&& other)
  26.         : name(move(other.name)), country(move(other.country)), year(other.year)
  27.     {
  28.         cout << "I am being moved.\n";
  29.     }
  30.  
  31.     ~President()
  32.     {
  33.         cout << "I am being destructed.\n";
  34.     }
  35.  
  36.     President& operator=(const President& other) = default;
  37. };
  38.  
  39. int main()
  40. {
  41.     // VS2013의 emplace_back
  42.     // vector 내부에서 생성 -> 컨테이터에 추가하기에 임시 객체 생성 X
  43.     vector<President> elections;
  44.     elections.emplace_back("Nelson Mandela""South Africa"1994);
  45.  
  46.     // VS2010의 emplace_back 역시 아래 push_back과 동일한 방식으로만 사용이 가능했었다
  47.     // 외부에서 생성 -> 벡터로 이동 -> 외부 객체 파괴가 발생한다
  48.     vector<President> reElections;
  49.     reElections.push_back(President("Franklin Delano Roosevelt""the USA"1936));
  50.  
  51.     for (President const& president: elections)
  52.     {
  53.         cout << president.name << " was elected president of "
  54.              << president.country << " in " << president.year << ".\n";
  55.     }
  56.     for (President const& president: reElections)
  57.     {
  58.         cout << president.name << " was re-elected president of "
  59.              << president.country << " in " << president.year << ".\n";
  60.     }
  61. }

std::map의 emplace 함수도 예제를 한번 살펴 보도록 하자.
아래 예제에서는 임시 객체가 한 번 덜 생성되는 것을 확인할 수 있다.

  1. typedef map<int, President> ElectionMap;
  2. ElectionMap elections;
  3.  
  4. ////////////////////////////////////////////////////////////////////////////////
  5. /// 기존의 insert
  6. ////////////////////////////////////////////////////////////////////////////////
  7. {
  8.     // p 객체 생성
  9.     President p("Nelson Mandela""South Africa"1994);
  10.  
  11.     // p 객체 복사 생성 for pair -> p 객체 이동
  12.     // 아래 두 문장은 동일하다
  13.     //elections.insert(ElectionMap::value_type(1, p));
  14.     elections.insert(make_pair(1, p));
  15.  
  16.     // 이 스코프가 종료되면,
  17.     // President p("Nelson Mandela", "South Africa", 1994);에서 생성된 객체 파괴
  18.     // ElectionMap::value_type(1, p)에서 생성된 임시 객체 파괴
  19.     // map 소멸되면서 보관되어 있던 원소 파괴
  20. }
  21.  
  22. ////////////////////////////////////////////////////////////////////////////////
  23. /// C++11의 emplace
  24. ////////////////////////////////////////////////////////////////////////////////
  25. {
  26.     // President 객체 생성 -> 객체 이동 후 바로 컨테이너에 추가
  27.     elections.emplace(1, President("Nelson Mandela""South Africa"1994));
  28.  
  29.     // 이 스코프가 종료되면
  30.     // President("Nelson Mandela", "South Africa", 1994)에서 생성된 객체 파괴
  31.     // map 소멸되면서 보관되어 있던 원소 파괴
  32. }

참고로, 각 STL 컨테이너들이 지원하는 emplace 계열 함수들은 다음과 같다.

1) vector
  • emplace
  • emplace_back
2) deque / list
  • emplace
  • emplace_back
  • emplace_front
3) foward_list
  • emplace_front
  • emplace_after
4) set / map / unoreder_set / unordered_map
  • emplace
  • emplace_hint



ref : http://egloos.zum.com/sweeper/v/3060229


반응형
반응형

05-5 내장 함수

지금까지 파이썬으로 프로그래밍하기 위해 알아야 하는 대부분의 것들을 공부했다. 이제 여러분은 자신이 원하는 프로그램을 직접 만들 수 있을 것이다. 하지만 그 전에 먼저 여러분이 만들고자 하는 프로그램이 이미 만들어져 있는지 살펴보기 바란다. 물론 공부를 위해서라면 누군가 만들어 놓은 프로그램을 또 만들 수도 있다. 하지만 그런 목적이 아니라면 이미 만들어진 것을 다시 만드는 것은 불필요한 행동이다(Don’t Reinvent The Wheel, 이미 있는 것을 다시 만드느라 쓸데없이 시간을 낭비하지 말라). 그리고 이미 만들어진 프로그램들은 테스트 과정을 수도 없이 거쳤기 때문에 충분히 검증되어 있다. 따라서 무엇인가 새로운 프로그램을 만들기 전에는 이미 만들어진 것들, 그중에서도 특히 파이썬 배포본에 함께 들어 있는 파이썬 라이브러리들을 살펴보는 것이 매우 중요하다.

라이브러리들을 살펴보기 전에 파이썬 내장 함수를 먼저 살펴보도록 하자. 우리는 이미 몇 가지 내장 함수들을 배웠다. print, del, type 등이 바로 그것이다. 이러한 파이썬 내장 함수들은 외부 모듈과는 달리 import를 필요로 하지 않는다. 아무런 설정 없이 바로 사용할 수가 있다. 이 책에서는 활용 빈도가 높고 중요한 함수들을 중심으로 알파벳 순서대로 간략히 정리했다. 파이썬으로 프로그래밍을 하기 위해 이 함수들을 지금 당장 모두 알아야 하는 것은 아니므로 가벼운 마음으로 천천히 살펴보자.

abs

abs(x)는 어떤 숫자를 입력으로 받았을 때, 그 숫자의 절대값을 돌려주는 함수이다.

>>> abs(3)
3
>>> abs(-3)
3
>>> abs(-1.2)
1.2

all

all(x)은 반복 가능한(iterable) 자료형 x를 입력 인수로 받으며, 이 x가 모두 참이면 True, 거짓이 하나라도 있 으면 False를 리턴한다.

(※ 반복 가능한 자료형이란 for문으로 그 값을 출력할 수 있는 것을 의미한다. 리스트, 튜플, 문자열, 딕셔너리, 집합 등이 있다.)

다음의 예를 보자.

>>> all([1, 2, 3])
True

리스트 자료형 [1, 2, 3]은 모든 요소가 참이므로 True를 리턴한다.

>>> all([1, 2, 3, 0])
False

리스트 자료형 [1, 2, 3, 0] 중에서 요소 0은 거짓이므로 False를 리턴한다.

(※ 자료형의 참과 거짓에 대해 잘 기억나지 않는다면 02-7절을 다시 한 번 읽어 보자.)

any

any(x)는 x 중 하나라도 참이 있을 경우 True를 리턴하고, x가 모두 거짓일 경우에만 False를 리턴한다. all(x)의 반대 경우라고 할 수 있다.

다음의 예를 보자.

>>> any([1, 2, 3, 0])
True

리스트 자료형 [1, 2, 3, 0] 중에서 1, 2, 3이 참이므로 True를 리턴한다.

>>> any([0, ""])
False

리스트 자료형 [0, ""]의 요소 0과 ""은 모두 거짓이므로 False를 리턴한다.

chr

chr(i)는 아스키(ASCII) 코드값을 입력으로 받아 그 코드에 해당하는 문자를 출력하는 함수이다.

(※ 아스키 코드란 0에서 127 사이의 숫자들을 각각 하나의 문자 또는 기호에 대응시켜 놓은 것이다.)

>>> chr(97)
'a'
>>> chr(48)
'0'

dir

dir은 객체가 자체적으로 가지고 있는 변수나 함수를 보여 준다. 아래 예는 리스트와 딕셔너리 객체의 관련 함수들(메서드)을 보여 주는 예이다. 우리가 02장에서 살펴보았던 자료형 관련 함수들을 만나볼 수 있을 것이다.

>>> dir([1, 2, 3])
['append', 'count', 'extend', 'index', 'insert', 'pop',...]
>>> dir({'1':'a'})
['clear', 'copy', 'get', 'has_key', 'items', 'keys',...]

divmod

divmod(a, b)는 2개의 숫자를 입력으로 받는다. 그리고 a를 b로 나눈 몫과 나머지를 튜플 형태로 리턴하는 함수이다.

>>> divmod(7, 3)
(2, 1)
>>> divmod(1.3, 0.2)
(6.0, 0.099999999999999978)

enumerate

enumerate는 "열거하다"라는 뜻이다. 이 함수는 순서가 있는 자료형(리스트, 튜플, 문자열)을 입력으로 받아 인덱스 값을 포함하는 enumerate 객체를 리턴한다.

(※ 보통 enumerate 함수는 아래 예제처럼 for문과 함께 자주 사용된다.)

무슨 말인지 잘 이해되지 않으면 다음 예를 보자.

>>> for i, name in enumerate(['body', 'foo', 'bar']):
...     print(i, name)
...
0 body
1 foo
2 bar

순서값과 함께 body, foo, bar가 순서대로 출력되었다. 즉, 위 예제와 같이 enumerate를 for문과 함께 사용하면 자료형의 현재 순서(index)와 그 값을 쉽게 알 수 있다.

for문처럼 반복되는 구간에서 객체가 현재 어느 위치에 있는지 알려주는 인덱스 값이 필요할때 enumerate 함수를 사용하면 매우 유용하다.

eval

eval(expression)은 실행 가능한 문자열(1+2, 'hi' + 'a' 같은 것)을 입력으로 받아 문자열을 실행한 결과값을 리턴하는 함수이다.

>>> eval('1+2')
3
>>> eval("'hi' + 'a'")
'hia'
>>> eval('divmod(4, 3)')
(1, 1)

보통 eval은 입력받은 문자열로 파이썬 함수나 클래스를 동적으로 실행하고 싶은 경우에 사용된다.

filter

filter란 무엇인가를 걸러낸다는 뜻으로, filter 함수도 동일한 의미를 가진다. filter 함수는 첫 번째 인수로 함수 이름을, 두 번째 인수로 그 함수에 차례로 들어갈 반복 가능한 자료형을 받는다. 그리고 두 번째 인수인 반복 가능한 자료형 요소들이 첫 번째 인수인 함수에 입력되었을 때 리턴값이 참인 것만 묶어서(걸러내서) 돌려준다.

다음의 예를 보자.

#positive.py 
def positive(l): 
    result = [] 
    for i in l: 
        if i > 0: 
            result.append(i) 
    return result

print(positive([1,-3,2,0,-5,6]))

결과값: [1, 2, 6]

즉, 위에서 만든 positive 함수는 리스트를 입력값으로 받아 각각의 요소를 판별해서 양수값만 리턴하는 함수이다.

filter 함수를 이용하면 위의 내용을 아래와 같이 간단하게 작성할 수 있다.

#filter1.py
def positive(x):
    return x > 0

print(list(filter(positive, [1, -3, 2, 0, -5, 6])))

결과값: [1, 2, 6]

여기서는 두 번째 인수인 리스트의 요소들이 첫 번째 인수인 positive 함수에 입력되었을 때 리턴값이 참인 것만 묶어서 돌려준다. 앞의 예에서는 1, 2, 6만 양수여서 x > 0 이라는 문장이 참이 되므로 [1, 2, 6]이라는 결과값을 리턴하게 된 것이다.

앞의 함수는 lambda를 이용하면 더욱 간편하게 코드를 작성할 수 있다.

>>> print(list(filter(lambda x: x > 0, [1, -3, 2, 0, -5, 6])))

hex

hex(x)는 정수값을 입력받아 16진수(hexadecimal)로 변환하여 리턴하는 함수이다.

>>> hex(234)
'0xea'
>>> hex(3)
'0x3'

id

id(object)는 객체를 입력받아 객체의 고유 주소값(레퍼런스)을 리턴하는 함수이다.

>>> a = 3
>>> id(3)
135072304
>>> id(a)
135072304
>>> b = a
>>> id(b)
135072304

위 예의 3, a, b는 고유 주소값이 모두 135072304이다. 즉, 3, a, b가 모두 같은 객체를 가리키고 있음을 알 수 있다.

만약 id(4)라고 입력하면 4는 3, a, b와 다른 객체이므로 당연히 다른 고유 주소값이 출력된다.

>>> id(4)
135072292

input

input([prompt])은 사용자 입력을 받는 함수이다. 입력 인수로 문자열을 주면 아래의 세 번째 예에서 볼 수 있듯이 그 문자열은 프롬프트가 된다.

(※ [ ] 기호는 괄호 안의 내용을 생략할 수 있다는 관례적인 표기법임을 기억하자.)

(※ 파이썬 2.7 버전의 경우 input 대신 raw_input을 사용해야 한다.)

>>> a = input()
hi
>>> a
'hi'
>>> b = input("Enter: ")
Enter: hi

위에서 입력받은 문자열을 확인해 보면 다음과 같다.

>>> b
'hi'

int

int(x)는 문자열 형태의 숫자나 소수점이 있는 숫자 등을 정수 형태로 리턴하는 함수로, 정수를 입력으로 받으면 그대로 리턴한다.

>>> int('3')
3
>>> int(3.4)
3

int(x, radix)는 radix 진수로 표현된 문자열 x를 10진수로 변환하여 리턴한다.

2진수로 표현된 '11'의 10진수 값은 다음과 같이 구한다.

>>> int('11', 2)
3

16진수로 표현된 '1A'의 10진수 값은 다음과 같이 구한다.

>>> int('1A', 16)
26

isinstance

isinstance(object, class)는 첫 번째 인수로 인스턴스, 두 번째 인수로 클래스 이름을 받는다. 입력으로 받은 인스턴스가 그 클래스의 인스턴스인지를 판단하여 참이면 True, 거짓이면 False를 리턴한다.

>>> class Person: pass
...
>>> a = Person()
>>> isinstance(a, Person)
True

위의 예는 a가 Person 클래스에 의해서 생성된 인스턴스임을 확인시켜 준다.

>>> b = 3
>>> isinstance(b, Person)
False

b는 Person 클래스에 의해 생성된 인스턴스가 아니므로 False를 리턴한다.

len

len(s)은 입력값 s의 길이(요소의 전체 개수)를 리턴하는 함수이다.

>>> len("python")
6
>>> len([1,2,3])
3
>>> len((1, 'a'))
2

list

list(s)는 반복 가능한 자료형 s를 입력받아 리스트로 만들어 리턴하는 함수이다.

>>> list("python")
['p', 'y', 't', 'h', 'o', 'n']
>>> list((1,2,3))
[1, 2, 3]

list 함수에 리스트를 입력으로 주면 똑같은 리스트를 복사하여 돌려준다.

>>> a = [1, 2, 3]
>>> b = list(a)
>>> b
[1, 2, 3]

map

map(f, iterable)은 함수(f)와 반복 가능한(iterable) 자료형을 입력으로 받는다. map은 입력받은 자료형의 각 요소가 함수 f에 의해 수행된 결과를 묶어서 리턴하는 함수이다.

다음의 예를 보자.

# two_times.py
def two_times(numberList):
    result = [ ]
    for number in numberList:
        result.append(number*2)
    return result

result = two_times([1, 2, 3, 4])
print(result)

two_times 함수는 리스트 요소를 입력받아 각 요소에 2를 곱한 결과값을 돌려준다. 실행 결과는 다음과 같다.

결과값: [2, 4, 6, 8]

위의 예제는 map 함수를 이용하면 다음처럼 바꿀 수 있다.

>>> def two_times(x): return x*2
...
>>> list(map(two_times, [1, 2, 3, 4]))
[2, 4, 6, 8]

이제 앞 예제를 해석해 보자. 먼저 리스트의 첫 번째 요소인 1이 two_times 함수의 입력값으로 들어가고, 1 * 2의 과정을 거쳐서 2가 된다. 다음으로 리스트의 두 번째 요소인 2가 2 * 2의 과정을 거쳐 4가 된다. 따라서 결과값 리스트는 이제 [2, 4]가 된다. 총 4개의 요소값이 모두 수행되면 최종적으로 [2, 4, 6, 8]이 리턴된다. 이것이 map 함수가 하는 일이다.

(※ 위 예에서 map의 결과를 리스트로 보여 주기 위해 list 함수를 이용하여 출력하였다. 파이썬 2.7은 map의 결과가 리스트이므로 위 예에서 list 함수를 이용하여 리스트로 변환하지 않아도 된다.)

앞의 예는 lambda를 사용하면 다음처럼 간략하게 만들 수 있다.

>>> list(map(lambda a: a*2, [1, 2, 3, 4]))
[2, 4, 6, 8]

map 함수 예를 하나 더 살펴보자.

# map_test.py
def plus_one(x):
    return x+1
print(list(map(plus_one, [1, 2, 3, 4, 5])))

결과값: [2, 3, 4, 5, 6]

위 예는 map과 plus_one 함수를 이용하여 리스트의 각 요소값을 1씩 증가시키는 예제이다.

max

max(iterable)는 인수로 반복 가능한 자료형을 입력받아 그 최대값을 리턴하는 함수이다.

>>> max([1, 2, 3])
3
>>> max("python")
'y'

min

min(iterable)은 max 함수와 반대로, 인수로 반복 가능한 자료형을 입력받아 그 최소값을 리턴하는 함수이다.

>>> min([1, 2, 3])
1
>>> min("python")
'h'

oct

oct(x)는 정수 형태의 숫자를 8진수 문자열로 바꾸어 리턴하는 함수이다.

>>> oct(34)
'0o42'
>>> oct(12345)
'0o30071'

open

open(filename, [mode])은 "파일 이름"과 "읽기 방법"을 입력받아 파일 객체를 리턴하는 함수이다. 읽기 방법(mode)이 생략되면 기본값인 읽기 전용 모드(r)로 파일 객체를 만들어 리턴한다.

mode설명
w쓰기 모드로 파일 열기
r읽기 모드로 파일 열기
a추가 모드로 파일 열기
b바이너리 모드로 파일 열기

b는 w, r, a와 함께 사용된다.

>>> f = open("binary_file", "rb")

위 예의 rb는 "바이너리 읽기 모드"를 의미한다.

아래 예의 fread와 fread2는 동일한 방법이다.

>>> fread = open("read_mode.txt", 'r')
>>> fread2 = open("read_mode.txt")

즉, 모드 부분이 생략되면 기본값으로 읽기 모드인 r을 갖게 된다.

다음은 추가 모드(a)로 파일을 여는 예이다.

>>> fappend = open("append_mode.txt", 'a')

ord

ord(c)는 문자의 아스키 코드값을 리턴하는 함수이다.

(※ ord 함수는 chr 함수와 반대이다.)

>>> ord('a')
97
>>> ord('0')
48

pow

pow(x, y)는 x의 y 제곱한 결과값을 리턴하는 함수이다.

>>> pow(2, 4)
16
>>> pow(3, 3)
27

range

range([start,] stop [,step])는 for문과 함께 자주 사용되는 함수이다. 이 함수는 입력받은 숫자에 해당되는 범위의 값을 반복 가능한 객체로 만들어 리턴한다.

인수가 하나일 경우

시작 숫자를 지정해 주지 않으면 range 함수는 0부터 시작한다.

>>> list(range(5))
[0, 1, 2, 3, 4]

인수가 2개일 경우

입력으로 주어지는 2개의 인수는 시작 숫자와 끝 숫자를 나타낸다. 단, 끝 숫자는 해당 범위에 포함되지 않는다는 것에 주의하자.

>>> list(range(5, 10))
[5, 6, 7, 8, 9]

인수가 3개일 경우

세 번째 인수는 숫자 사이의 거리를 말한다.

>>> list(range(1, 10, 2))
[1, 3, 5, 7, 9]
>>> list(range(0, -10, -1))
[0, -1, -2, -3, -4, -5, -6, -7, -8, -9]

round

round(number[, ndigits]) 함수는 숫자를 입력받아 반올림 해 주는 함수이다.

>>> round(4.6)
5
>>> round(4.2)
4

만약 5.678 라는 실수를 소수점 2자리까지만 반올림하여 표시하고 싶다면 다음과 같이 사용할 수 있다.

>>> round(5.678, 2)
5.68

round 함수의 두번째 파라미터는 반올림하여 표시하고 싶은 소수점의 자리수(ndigits)이다.

sorted

sorted(iterable) 함수는 입력값을 정렬한 후 그 결과를 리스트로 리턴하는 함수이다.

>>> sorted([3, 1, 2])
[1, 2, 3]
>>> sorted(['a', 'c', 'b'])
['a', 'b', 'c']
>>> sorted("zero")
['e', 'o', 'r', 'z']
>>> sorted((3, 2, 1))
[1, 2, 3]

리스트 자료형에도 sort라는 함수가 있다. 하지만 리스트 자료형의 sort 함수는 리스트 객체 그 자체를 정렬만 할 뿐 정렬된 결과를 리턴하지는 않는다.

다음 예제로 sorted 함수와 리스트 자료형의 sort 함수의 차이점을 확인해 보자.

>>> a = [3, 1, 2]
>>> result = a.sort()
>>> print(result)
None
>>> a
[1, 2, 3]

sort 함수는 리턴값이 없기 때문에 result 변수에 저장되는 값이 없다. 따라서 print(result)를 하면 None이 출력된다. sort 함수를 수행한 후 리턴값은 없지만 리스트 객체 a를 확인하면 [3, 1, 2]가 [1, 2, 3]으로 정렬된 것을 볼 수 있다.

str

str(object)은 문자열 형태로 객체를 변환하여 리턴하는 함수이다.

>>> str(3)
'3'
>>> str('hi')
'hi'
>>> str('hi'.upper())
'HI'

tuple

tuple(iterable)은 반복 가능한 자료형을 입력받아 튜플 형태로 바꾸어 리턴하는 함수이다. 만약 튜플이 입력으로 들어오면 그대로 리턴한다.

>>> tuple("abc")
('a', 'b', 'c')
>>> tuple([1, 2, 3])
(1, 2, 3)
>>> tuple((1, 2, 3))
(1, 2, 3)

type

type(object)은 입력값의 자료형이 무엇인지 알려주는 함수이다.

>>> type("abc")
<class 'str'>
>>> type([ ])
<class 'list'>
>>> type(open("test", 'w'))
<class '_io.TextIOWrapper'>

zip

zip(iterable*)은 동일한 개수로 이루어진 자료형을 묶어 주는 역할을 하는 함수이다.

잘 이해되지 않는다면 다음 예제를 살펴보자.

>>> list(zip([1, 2, 3], [4, 5, 6]))
[(1, 4), (2, 5), (3, 6)]
>>> list(zip([1, 2, 3], [4, 5, 6], [7, 8, 9]))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
>>> list(zip("abc", "def"))
[('a', 'd'), ('b', 'e'), ('c', 'f')]

연습문제

(연습문제 풀이 : https://wikidocs.net/17090#05-5)


[문제1] 내장함수

다음 결과를 예측하시오.

1)

>>> all([1, 2, abs(-3)-3])

2)

>>> chr(ord('a')) == 'a'


[문제2] enumerate

enumerate 내장함수를 이용하여 ['a', 'b', 'c']라는 리스트를 {0:'a', 1:'b', 2:'c'}라는 딕셔너리로 바꾸시오.


[문제3] filter와 lambda

filter와 lambda를 이용하여 [1, -2, 3, -5, 8, -3]라는 리스트에서 음수를 모두 제거하시오.


[문제4] 16진수를 10진수로 변환

234라는 10진수의 16진수는 다음과 같이 구할 수 있다.

>>> hex(234)
'0xea'

이번에는 반대로 '0xea' 라는 16진수 문자열을 10진수로 변경해 보시오. (힌트. 내장함수 int를 활용해 보자.)


[문제5] map과 lambda

map과 lambda를 이용하여 [1, 2, 3, 4] 라는 리스트의 각 요소값에 3이 곱해진 [3, 6, 9, 12]라는 리스트를 만드시오.


[문제6] 최대값과 최소값

다음 리스트의 최대값과 최소값의 합을 구하시오.

[-8, 2, 7, 5, -3, 5, 0, 1]


[문제7] 소수점 반올림

17 / 3 의 결과는 다음과 같다.

>>> 17 / 3
5.666666666666667

위와 같은 결과값 5.666666666666667을 소숫점 4자리까지만 반올림하여 표시하시오.


[문제8] zip

[1, 2, 3, 4]와 ['a', 'b', 'c', 'd']라는 리스트가 있다. 이 두개의 리스트를 합쳐 다음과 같은 리스트를 만드시오. (힌트. 내장함수 zip을 이용해 보자.)

[(1, 'a'), (2, 'b'), (3, 'c'), (4, 'd')]


ref : https://wikidocs.net/32#max

반응형
반응형


VS2017 기준 : C++ 17 활성화 옵션 



 프로젝트 속성페이제어서 다음처럼 원하는 C++ 버전을 선택해주면 된다










Add to Additional options in Project Settings: /std:c++latest to enable latest features - currently C++17 as of VS2017, VS2015 Update 3.

https://blogs.msdn.microsoft.com/vcblog/2016/06/07/standards-version-switches-in-the-compiler/

/permissive- will disable non-standard C++ extensions and will enable standard conformance in VS2017.

https://blogs.msdn.microsoft.com/vcblog/2016/11/16/permissive-switch/

EDIT (Nov 2017): Latest VS2017 features are documented here: https://docs.microsoft.com/en-gb/cpp/build/reference/std-specify-language-standard-version

VS2017 supports now: /std:[c++14|c++17|c++latest] and these flags can be set via the project's property pages:

To set this compiler option in the Visual Studio development environment

  1. Open the project's Property Pages dialog box. For details, see Working with Project Properties.
  2. Select Configuration Properties, C/C++, Language.
  3. In C++ Language Standard, choose the language standard to support from the dropdown control, then choose OK or Apply to save your changes.


https://stackoverflow.com/questions/41308933/how-to-enable-c17-compiling-in-visual-studio

반응형
반응형


Rvalue References: C++0x Features in VC10, Part 2



Today, I’m going to talk about rvalue references, which enable two different things: move semantics and perfect forwarding.  This post will be long, because I’m going to explain how rvalue references work in great detail.  They’re initially very confusing because they distinguish lvalues from rvalues, which very few C++98/03 programmers are extensively familiar with.

 

Fear not, for using rvalue references is easy, much easier than it initially sounds.  Implementing either move semantics or perfect forwarding in your own code boils down to following simple patterns, which I will demonstrate.  And it’s definitely worth learning how to use rvalue references, as move semantics can produce order of magnitude performance improvements, and perfect forwarding makes writing highly generic code very easy.

 

 

lvalues and rvalues in C++98/03

 

In order to understand rvalue references in C++0x, you must first understand lvalues and rvalues in C++98/03.

 

The terminology of “lvalues” and “rvalues” is confusing because their history is confusing.  (By the way, they’re just pronounced as “L values” and “R values”, although they’re written as single words.)  These concepts originally came from C, and then were elaborated upon by C++.  To save time, I’ll skip over their history, including why they’re called “lvalues” and “rvalues”, and I’ll go directly to how they work in C++98/03.  (Okay, it’s not a big secret: “L” stands for “left” and “R” stands for “right”.  But the concepts have evolved since the names were chosen, and the names aren’t very accurate anymore.  Instead of going through the whole history lesson, you can consider the names to be arbitrary like “up quark” and “down quark”, and you won’t lose anything.)

 

C++03 3.10/1 says: “Every expression is either an lvalue or an rvalue.”  It’s important to remember that lvalueness versus rvalueness is a property of expressions, not of objects.

 

Lvalues name objects that persist beyond a single expression.  For example, obj , *ptr , ptr[index] , and ++x are all lvalues.

 

Rvalues are temporaries that evaporate at the end of the full-expression in which they live (“at the semicolon”).  For example, 1729 , x + y , std::string(“meow”) , and x++ are all rvalues.

 

Notice the difference between ++x and x++ .  If we have int x = 0; then the expression x is an lvalue, as it names a persistent object.  The expression ++x is also an lvalue.  It modifies and then names the persistent object.  However, the expression x++ is an rvalue.  It copies the original value of the persistent object, modifies the persistent object, and then returns the copy.  This copy is a temporary.  Both ++x and x++increment x, but ++x returns the persistent object itself, while x++ returns a temporary copy.  That’s why ++x is an lvalue, while x++ is an rvalue.  Lvalueness versus rvalueness doesn’t care about what an expression does, it cares about what an expression names (something persistent or something temporary).

 

If you want to build up intuition for this, another way to determine whether an expression is an lvalue is to ask “can I take its address?”.  If you can, it’s an lvalue.  If you can’t, it’s an rvalue.  For example, &obj , &*ptr , &ptr[index] , and &++x are all valid (even though some of those expressions are silly), while &1729 , &(x + y) , &std::string(“meow”) , and &x++ are all invalid.  Why does this work?  The address-of operator requires that its “operand shall be an lvalue” (C++03 5.3.1/2).  Why does it require that?  Taking the address of a persistent object is fine, but taking the address of a temporary would be extremely dangerous, because temporaries evaporate quickly.

 

The preceding examples ignore operator overloading, which is convenient syntax for a function call.  “A function call is an lvalue if and only if the result type is a reference.” (C++03 5.2.2/10)  Therefore, given vector<int> v(10, 1729); , v[0] is an lvalue because operator[]()returns int& (and &v[0] is valid and useful), while given string s(“foo”); and string t(“bar”); , s + t is an rvalue because operator+() returns string (and &(s + t) is invalid).

 


 

Type& binds to modifiable lvalues (and can be used to observe and mutate them).  It can’t bind to const lvalues, as that would violate constcorrectness.  It can’t bind to modifiable rvalues, as that would be extremely dangerous.  Accidentally modifying temporaries, only to have the temporaries evaporate along with your modifications, would lead to subtle and obnoxious bugs, so C++ rightly prohibits this.  (I should mention that VC has an evil extension that allows this, but if you compile with /W4 , it warns when the evil extension is activated.  Usually.)  And it can’t bind to const rvalues, as that would be doubly bad.  (Careful readers should note that I’m not talking about template argument deduction here.)

 

const Type& binds to everything: modifiable lvalues, const lvalues, modifiable rvalues, and const rvalues (and can be used to observe them).

 

A reference is a name, so a reference bound to an rvalue is itself an lvalue (yes, L).  (As only a const reference can be bound to an rvalue, it will be a const lvalue.)  This is confusing, and will be an extremely big deal later, so I’ll explain further.  Given the function void observe(const string& str) , inside observe()‘s implementation, str is a const lvalue, and its address can be taken and used before observe() returns.  This is true even though observe() can be called with rvalues, such as three() or four() above.  observe(“purr”) can also be called, which constructs a temporary string and binds str to that temporary.  The return values of three() and four() don’t have names, so they’re rvalues, but within observe()str is a name, so it’s an lvalue.  As I said above, “lvalueness versus rvalueness is a property of expressions, not of objects”.  Of course, because str can be bound to a temporary which will evaporate, its address shouldn’t be stored anywhere where it could be used after observe() returns.

 

Have you ever bound an rvalue to a const reference and then taken its address?  Yes, you have!  This is what happens when you write a copy assignment operator, Foo& operator=(const Foo& other) , with a self-assignment check, if (this != &other) { copy stuff; } return *this; , and you copy assign from a temporary, like Foo make_foo(); Foo f; f = make_foo(); .

 

At this point, you might ask, “So what’s the difference between modifiable rvalues and const rvalues?  I can’t bind Type& to modifiable rvalues, and I can’t assign things (etc.) to modifiable rvalues, so can I really modify them?”  This is a very good question!  In C++98/03, the answer is that there’s a slight difference: non-const member functions can be called on modifiable rvalues.  C++ doesn’t want you to accidentally modify temporaries, but directly calling a non-const member function on a modifiable rvalue is explicit, so it’s allowed.  In C++0x, the answer changes dramatically, making move semantics possible.

 

Congratulations!  Now you have what I call “lvalue/rvalue vision”, the ability to look at an expression and determine whether it’s an lvalue or an rvalue.  Combined with your “const vision”, you can precisely reason that given void mutate(string& ref) and the definitions above, mutate(one) is valid, while mutate(two)mutate(three())mutate(four()), and mutate(“purr”) are invalid, and all of observe(one)observe(two)observe(three())observe(four()), and observe(“purr”) are valid.  If you’re a C++98/03 programmer, you already knew which of these calls were valid and which were invalid; your “gut feeling”, if not your compiler, would have told you that mutate(three()) was bogus.  Your new lvalue/rvalue vision tells you precisely why (three() is an rvalue, and modifiable references can’t be bound to rvalues).  Is that useful?  To language lawyers, yes, but not really to normal programmers.  After all, you’ve gotten this far without knowing all of this stuff about lvalues and rvalues.  But here’s the catch: compared to C++98/03, C++0x has vastly more powerful lvalue/rvalue vision (in particular, the ability to look at an expression, determine whether it’s a modifiable/const lvalue/rvalue, and do something about it).  In order to use C++0x effectively, you need lvalue/rvalue vision too.  And now you have it, so we can proceed!

 

 

the copying problem

 

C++98/03 combines insanely powerful abstraction with insanely efficient execution, but it has a problem: it’s overly fond of copying.  Things with value semantics behave like ints, so copying a thing doesn’t modify the source, and the resulting copies are independent.  Value semantics are great, except that they tend to lead to unnecessary copies of heavy objects like strings, vectors, and so forth.  (“Heavy” means “expensive to copy”; a million-element vector is heavy.)  The Return Value Optimization (RVO) and Named Return Value Optimization (NRVO), where copy constructors are elided in certain situations, help to alleviate this problem, but they don’t remove all unnecessary copies.

 

The most unnecessary copies are those where the source is about to be destroyed.  Would you photocopy a sheet of paper and then immediately throw away the original, assuming that the original and the photocopy are identical?  That would be wasteful; you should keep the original and not bother with the photocopy.  Here’s what I call “the killer example”, derived from one of the Standardization Committee’s examples (in N1377).  Suppose that you have a bunch of strings, like this:

 

string s0(“my mother told me that”);

string s1(“cute”);

string s2(“fluffy”);

string s3(“kittens”);

string s4(“are an essential part of a healthy diet”);

 

And that you concatenate them like this:

 

string dest = s0 + ” ” + s1 + ” ” + s2 + ” ” + s3 + ” ” + s4;

 

How efficient is this?  (We’re not worrying about this specific example, which executes in microseconds; we’re worrying about its generalization, which occurs throughout the entire language.)

 

Each call to operator+() returns a temporary string.  There are 8 calls to operator+() , so there are 8 temporary strings.  Each one, upon its construction, performs a dynamic memory allocation and copies all of the characters that have been concatenated so far, and later, upon its destruction, performs a dynamic memory deallocation.  (If you’ve heard of the Small String Optimization, which VC performs in order to avoid dynamic memory allocations and deallocations for short strings, it’s defeated here by my carefully chosen and sufficiently long s0 , and even if it applied, it couldn’t avoid the copying.  If you’ve heard of the Copy-On-Write “optimization”, forget about it – it doesn’t apply here, and it’s a pessimization under multithreading, so Standard Library implementations don’t do it anymore.)

 

In fact, because every concatenation copies all of the characters that have been concatenated so far, this has quadratic complexity in the number of concatenations.  Yuck!  This is extraordinarily wasteful, which is especially embarrassing for C++.  Why is this happening, and what can we do about it?

 

The problem is that operator+() , which takes two const string& or one const string& and one const char * (there are other overloads, which we aren’t using here), can’t tell whether it’s being fed lvalues versus rvalues, so it always has to create and return a new temporary string .  Why do lvalues versus rvalues matter?

 

When evaluating s0 + ” “ , it’s absolutely necessary to create a new temporary string .  s0 is an lvalue, naming a persistent object, so we can’t modify it.  (Someone would notice!)  But when evaluating (s0 + ” “) + s1 , we could simply append s1‘s contents onto our first temporary string, instead of creating a second temporary and throwing the first temporary away.  This is the key insight behind move semantics: because s0 + ” “ is an rvalue, an expression referring to a temporary object, no one else in the entire program can observe that temporary object.  If we could detect that expression as being a modifiable rvalue, we could then proceed to modify the temporary object arbitrarily, without anyone else noticing.  operator+() isn’t “supposed to” modify its arguments, but if they’re modifiable rvalues, who cares?  In this manner, each call to operator+() can append characters onto a single temporary string .  This completely eliminates the unnecessary dynamic memory management and unnecessary copying, leaving us with linear complexity.  Yay!

 

Technically speaking, in C++0x, each call to operator+() still returns a separate temporary string .  However, the second temporary string(from evaluating (s0 + ” “) + s1 ) is constructed by stealing the memory owned by the first temporary string (from evaluating s0 + ” “) and then appending s1‘s contents onto that memory (which may trigger an ordinary geometric reallocation).  “Stealing” consists of pointer twiddling: the second temporary copies and then nulls out the first temporary’s internal pointer.  When the first temporary is eventually destroyed (“at the semicolon”), its pointer is null, so its destructor does nothing.

 

In general, being able to detect modifiable rvalues allows you to engage in “resource pilfering”.  If the objects referred to by modifiable rvalues own any resources (such as memory), you can steal their resources instead of copying them, since they’re going to evaporate anyways.  Constructing from or assigning from modifiable rvalues by taking what they own is generically referred to as “moving”, and moveable objects have “move semantics”.

 

This is extremely useful in many places, such as vector reallocation.  When a vector needs more capacity (e.g. during push_back()) and undergoes reallocation, it needs to copy elements from



ref : https://blogs.msdn.microsoft.com/vcblog/2009/02/03/rvalue-references-c0x-features-in-vc10-part-2/

반응형
반응형



결과는 컴파일러나 모드에 따라 다를 수 있지만 전체 문맥적으로 본다면 이해하는데 큰 무리는 없을겁니다





C/C++를 써야만 하는 혹은 쓸 수 밖에 없는 상황들이라는 것은 대부분 성능,속도, 자원 사용의 최적화 등이지요. Visual Studio 2010에 새로이 추가된 Rvalue concept 이나 사용 예들도 정확히 이러한 상황들에 대응하기 위한 것이라 할 수 있을 겁니다.

Rvalue의 경우에도 class의 개발 시 move constructor/operator 를 추가하여,  method를 호출할 때, 매개변수를 통하여 객체를 전달하는 과정에서 임시객체의 생성/소멸을 가능한 막아보자는 것이지요. 하지만 이러한 최적화 과정은 사실 Rvalue concept의 도입이전에도 다양한 형태로 시도되고, 실제 컴파일러에 적용되었습니다. 오늘 설명 드리고자 하는 (N)RVO 도 Rvalue와 비슷한 맥락의 최적화 기법 중 하나입니다.

간단한 예제를 통해서 그 내용을 살펴 볼까 합니다.


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
#include <stdio.h>
 
class RVO
{
public:
    RVO()
    {
        printf("I am in constructor\n");
    }
 
    RVO (const RVO& c_RVO) 
    {
        printf ("I am in copy constructor\n");
    }
    ~RVO()
    {
        printf ("I am in destructor\n");
    }
 
    int mem_var;       
};
 
RVO MyMethod (int i)
{
    RVO rvo;
    rvo.mem_var = i;
    return (rvo);
}
 
int _tmain(void)
{
    RVO rvo;
    rvo = MyMethod(5);
 
    return 0;
}




위 예제를 머리 속으로 컴파일 해서 수행해 보시고 출력 결과가 어떠할지, 그리고 생성자, 복사 생성자, 파괴자가 각각 몇 번이나 호출될지 정확히 예측하실 수 있으실까요? 위 source를 어떠한 optimiation도 켜지 않은 상태로, debug mode에서 수행하면 다음과 같은 결과를 출력합니다.


I am in constructor         <-- _tmain의 RVO rvo; 행에 의해서 호출될 겁니다. 
I am in constructor <-- MyMethod 함수의 RVO rvo; 행에 의해서 호출될 겁니다.
I am in copy constructor  <-- 이 녀석은 어디서 호출되는 걸까요? MeThod 함수의 return (rvo); 에서 호출됩니다.
I am in destructor
I am in destructor
I am in destructor

생성자, 복사 생성자, 파괴자는 총 6회 호출 되었음을 알 수 있죠. 어디서 생성자, 복사 생성자, 파괴자가 생성되었는지를 좀 더 명확하게 살펴보려면 위 코드를 컴파일 한 후 assembly code를 드려다 보면 좋습니다. 

먼저 _tmain() 부터 확인해보죠.


    RVO rvo; 

00F41518  lea         ecx,[ebp-10h]  // rvo의 위치는 [ebp-10h] 입니다. 
00F4151B  call        RVO::RVO (0F41140h)  // 생성자가 호출되었군요 1 
00F41520  mov         dword ptr [ebp-4],0  
    rvo = MyMethod(5); 
00F41527  push        5  
00F41529  lea         eax,[ebp-58h]  
// 5는 [ebp-58h]에 저장됩니다. 
00F4152C  push        eax  
// stack에 push하구요. 
00F4152D  call        MyMethod (0F410DCh)  // 함수를 호출했습니다. 
00F41532  add         esp,8  // stack을 정리 했구요. 
00F41535  mov         dword ptr [ebp-5Ch],eax  // 반환 값을 [ebp-5ch]로 담고는 
00F41538  mov         ecx,dword ptr [ebp-5Ch]  // 그 값을 다시 ecx로 옮깁니다. 
00F4153B  mov         edx,dword ptr [ecx]    // ecx가 가리키는 메모리의 값을 edx로 옮기고 
00F4153D  mov         dword ptr [ebp-10h],edx  // rvo 변수가 가리킬 수 있도록 변경합니다. 
00F41540  lea         ecx,[ebp-58h]  // 이건 반환되었던 객체죠? 
00F41543  call        RVO::~RVO (0F4101Eh) // 반환 되었던 객체의 파괴자를 호출합니다. 5

    return 0; 
00F41548  mov         dword ptr [ebp-54h],0  
00F4154F  mov         dword ptr [ebp-4],0FFFFFFFFh  
00F41556  lea         ecx,[ebp-10h]  // rvo 입니다. 
00F41559  call        RVO::~RVO (0F4101Eh) // rvo에 대한 파괴자를 호출하는군요. 6 
00F4155E  mov         eax,dword ptr [ebp-54h]  


이젠 MyMethod도 살펴 보겠습니다.



    RVO rvo; 
00F413EF  lea         ecx,[ebp-10h]  // rvo의 위치는 [ebp-10h] 입니다. 
00F413F2  call        RVO::RVO (0F41140h)  // 생성자가 호출되었군요. 2 
00F413F7  mov         dword ptr [ebp-4],1  
    rvo.mem_var = i; 
00F413FE  mov         eax,dword ptr [ebp+0Ch]  
00F41401  mov         dword ptr [ebp-10h],eax  
    return (rvo); 
00F41404  lea         eax,[ebp-10h]  
00F41407  push        eax  
00F41408  mov         ecx,dword ptr [ebp+8]  // [ebp+8] 위치의 임시 객체에 대해서 
00F4140B  call        RVO::RVO (0F41145h)  // 복사 생성자를 호출하는군요. 3 
00F41410  mov         ecx,dword ptr [ebp-54h]  
00F41413  or          ecx,1  
00F41416  mov         dword ptr [ebp-54h],ecx  
00F41419  mov         byte ptr [ebp-4],0  
00F4141D  lea         ecx,[ebp-10h]  // [ebp-10h] 위치의 객체는 rvo 입니다. 
00F41420  call        RVO::~RVO (0F4101Eh) // 파괴자를 호출하는군요 4
 
00F41425  mov         eax,dword ptr [ebp+8]  
}


(복사)생성자, 파괴자의 호출 code는 붉은색으로 표시하였고, 옆에 호출 순서에 따라 번호를 써 두었습니다. 근데 여기서 우리가 유심히 살펴보았음 직한 녀석은 3번의 복사 생성자 호출과 5번의 파괴자 호출입니다. 조금만 고민해 보면 MyMethod()에서 객체를 반환하기 위해서 임시 객체를 생성하고, 생성된 임시 객체를 반환한 후, 생성된 임시 객체의 파괴자를 호출하는 일련의 과정은 없어도 될 것 같지 않으세요? 이미 _tmain에서 생성된 객체가 있으므로, 임시 객체의 생성/파괴는 사실 불필요 하죠. 

임시 객체를 생성해야 했던 이유는 함수의 반환 값으로 객체를 전달하기 위해서만 쓰였잖아요. 이제 제가 말씀 드리고 싶어한 걸 설명할 시간이 되었네요. 

RVO는 Return Value Optimization 이라는 기법인데요. 이것이 뭔고 하니 위 예와 같이 어떤 함수가 객체를 반환해야 할 경우에 필요하지 않은 임시 객체를 생성하지 않도록 최적화 하는걸 말합니다. 그런데 RVO는 MyMethod() 에서 처럼 반환 객체가 변수명을 가지는 경우는 최적화가 되질 않았어요. 그래서 몇몇 사람들이 “변수가 이름을 가지는 경우에도 최적화의 대상에 포함시키자”로 주장했고, 이를 NRVO, Named Return Value Optimization 이라고 구분하여 불렀습니다. 그리하여 ISO/ANSI C++ 위원회에서 1996년에 이 이 둘 모두에 대해서 최적화 될 수 있음을 발표했다고 하는군요.(사실 발표하는 것이 뭐 어렵습니까? compiler 개발사만 어렵지..) 여하둥둥 그리하여 Visual Studio 2005에서 NRVO에 대한 최적화 기능이 포함되었습니다.


Visual Studio에서 NRVO를 가능하게 하기 위해서는 /O2 compiler option을 주면 됩니다.
(프로젝트 설정에서 Optimization : Maximize Speed를 선택하시면 됩니다.) 


vs2017 에서 Release 모드인경우 O2가 default 

Debug mode 에서는 '사용 않함 /Od' 가 기본 모드

이제 최적화의 결과물을 살펴 보시죠. 

I am in constructor 
I am in constructor 
I am in destructor 
I am in destructor


위의 출력결과와 사뭇 다른 것은 복사생성자의 호출이 빠졌고, 파괴자의 호출이 하나 줄어들었음을 알 수 있습니다.
(어느 부분이 생략되었는지 예측하실 수 있겠죠?)




내용을 명확하게 하기 위해서 컴파일된 결과물을 disassmebly 한 결과


MyMethod() 입니다.


RVO MyMethod (int i) 

    RVO rvo; 
01351030  push        esi  
01351031  call        RVO::RVO (1351000h)  // rvo 객체 생성자 호출이 여기 있군요. 
    rvo.mem_var = i; 
01351036  mov         dword ptr [esi],5  // 여기도 중요하죠. esi를 통해서 전달된 객체에다가 5를 넣어버립니다. 객체생성이 없죠. 
    return (rvo); 
0135103C  mov         eax,esi  

0135103E  ret 




_tmain() 입니다.



    RVO rvo; 
01351045  lea         eax,[rvo]  
01351048  push        eax  
01351049  call        RVO::RVO (1351000h)  // rvo 객체 생성자 호출이지요 
    rvo = MyMethod(5); 
0135104E  lea         esi,[rvo]  // 여기가 핵심입니다. 함수의 호출 결과를 rvo로 할당하는게 아니라,
                             rvo 값을 esi를 통해서 MyMethod로 넘겨버립니다.
 
01351051  call        MyMethod (1351030h)  
01351056  call        RVO::~RVO (1351020h)  // 파괴자 호출

    return 0; 
0135105B  call        RVO::~RVO (1351020h)  // 파괴자 호출 
01351060  xor         eax,eax  
01351062  pop         esi  

01351063  mov         esp,ebp  
01351065  pop         ebp  
01351066  ret  



여기저기 최적화 루틴 때문에, 앞서의 코드와는 많이 달라졌지만, 어떤 식으로 최적화가 진행되었는지를 미루어 짐작해 볼 수 있습니다. 







1. RVO

MEC++ 20항목에 나오는 예제를 그대로 활용해보자.


  1. class Rational
  2. {
  3. public:
  4.         Rational(int numerator = 0int denominator = 1);
  5.         ...
  6.         int numerator() const;
  7.         int denominator() const;
  8. };
  9.  
  10. const Rational operator * (const Rational &lhs, const Rational &rhs);

위에서 operator * 는 Rational 객체를 값으로 (const) 반환하고 있다.
객체를 값으로 반환하기 때문에 임시 객체 생성이 불가피한 것이다.


  1. // 1.
  2. const Rational* operator * (const Rational &lhs, const Rational &rhs);
  3. // 2.
  4. const Rational& operator * (const Rational &lhs, const Rational &rhs);


객체를 값으로 반환하는 함수에 대해 원론적으로 임시 객체 생성을 피할 수는 없다.
다만, 최신의 컴파일러들이 지원하는 RVO 최적화를 이용하는 길이 있을 뿐이다.

최적화를 이용하는 그 방법은 바로 객체 대신에 객체 생성자 인자를 반환하는 것이다.

  1. inline const Rational operator * (const Rational &lhs, const Rational &rhs)
  2. {
  3.         return Rational(lhs.numerator * rhs.numerator,
  4.                         lhs.denominator * rhs.denominator);
  5. }

얼핏 보기에 위 코드가 무슨 이득이 있나 싶다.

operator * 안에서 객체가 1번 만들어지고 다시 이것을 반환하면서 임시 객체까지 만들어지니
오히려 손해일 것 같다는 느낌이 팍팍 드는데 말이다.

하지만, 컴파일러는 반환시 임시 객체를 없애고,
계산 결과값을 반환값을 받는 객체에 대해 할당된 메모리에 직접 넣어 초기화해 준다.
결국 생성자 한번, 소멸자 한번의 호출 비용만 들어가는 것이다.

이것이 바로 RVO(Return Value Optimization)인 것이다.

RVO는 단독 형태의 연산자 오버로딩 구현에도 톡톡히 아름답게 쓰일 수 있다.

단독 형태의 (산술) 연산자는 결과만 가지고 값을 반환하기 때문에 임시 객체가 만들어진다.
(operator + 나 operator - 같은...)
그리고 대입 형태 연산자는 lhs에 값을 기록하고 반환하기 때문에 임시 객체를 만들 필요가 없다.
(operator += 나 operator -= 같은...)

int a, b, c, d, result;
result = a + b + c + d; 

보다는

result = a;
result += b;
result += c;
result += d; 가 더 효율 측면에서만 보면 낫다는 것이다.

물론 위의 예는 극단적인 것이다. 

평소에 효율을 위해 저렇게 코딩하지는 않을테니.말이 그렇다는 것이다. 
하지만, operator + 를 oerator += 를 이용하여 다음과 같이 구현한다면...

  1. template<typename T>
  2. opeator + (const T& lhs, const T& rhs)
  3. {
  4.         return T(lhs) += rhs;
  5. }

대입 형태 연산자의 특성과 RVO의 특성이 합쳐져서 임시 객체 생성이 전혀 이루어지지 않게 된다.
(아~ 물론 lhs와 rhs는 서로 대입이 가능한 pair 여야만 한다)


2. NRVO (Named RVO)

vs2005부터 지원되는 NRVO는 말 그대로 이름이 있는 변수에 대해서도 RVO가 적용되는 것이다.
RVO와는 다르게 NRVO는 최적화 옵션 /O1(크기 최소화)부터 동작함을 기억하자.

최적화 모드에 따라 달라짐 Debug , Release 모드 기본 최적화 옵션이 다른데 각각 모드를 변경해보면 차이를 알 수 있음
vs2017 기준 
debug : 사용 안 함(/Od)
Release : 최대 최적화(속도 우선)(/O2)


바로 예제부터 살펴보자.

  1. class RVOSample
  2. {
  3. public:
  4.     RVOSample();
  5.     ~RVOSample();
  6.  
  7.     int value;
  8. };
  9.  
  10. RVOSample TestNRVO(int num)
  11. {
  12.     RVOSample rvo;
  13.     rvo.value = num;
  14.     return rvo;    // 임시 객체가 생성되지 않는다.
  15. }

RVO와 다르게 변수를 선언하고, 해당 변수를 사용한 다음 반환해도 임시 객체가 생성되지 않는다.

RVO에 비해 훨씬 쓰게 편하고 보기도 익숙하다.
하지만, 아래 예제와 같은 경우엔 NRVO로 최적화되지 않는다.

  1. RVOSample TestNRVO(int num)
  2. {
  3.     RVOSample rvo;
  4.     rvo.value = num;
  5.  
  6.     /- 조건에 의해 반환값이 다른 경우 NRVO로 최적화되지 않는다. *-
  7.     if (5 == num)
  8.     {
  9.         rvo.value *= 2;
  10.         return rvo;
  11.     }
  12.  
  13.     return rvo;
  14. }

이 점만 유의하면, 중간에 어떠한 코드들이 들어가도 임시 객체는 생성되지 않는다.


3. RVO / NRVO 종합 예제

  1. #include "stdafx.h"
  2. #include <iostream>
  3.  
  4. class RVOTest
  5. {
  6. public:
  7.     RVOTest() { std::cout << "Constructor\n"; }
  8.     RVOTest(const RVOTest& rhs) { std::cout << "Copy Constructor\n"; }
  9.     RVOTest(RVOTest&& rhs) { std::cout << "Move Constructor\n"; }
  10.  
  11.     ~RVOTest() { std::cout << "Destructor\n"; }
  12. };
  13.  
  14. RVOTest RVOFunc()
  15. {
  16.     return RVOTest();
  17. }
  18.  
  19. RVOTest NRVOFunc()
  20. {
  21.     RVOTest r;
  22.     return r;
  23. }
  24.  
  25. int _tmain(int argc, _TCHAR* argv[])
  26. {
  27.     /*
  28.         Constructor
  29.         Destructor
  30.     */
  31.     {
  32.         RVOTest r1 = RVOFunc();                
  33.     }
  34.  
  35.     std::cout << "=============================\n";
  36.  
  37.    //이건 최적화 모드에 따라 달라짐 Debug , Release 모드 기본 최적화 옵션이 다른데 각각 모드를 변경해보면 차이를 알 수 있음
  38.     /*
  39.         Constructor
  40.         Destructor
  41.     */
  42.     {
  43.         RVOTest r2 = NRVOFunc();
  44.     }
  45.  
  46.     return 0;
  47. }




ref : http://egloos.zum.com/himskim/v/3630181

ref : http://egloos.zum.com/sweeper/v/1942099





반응형
반응형

1. Func? Action?


프로그램을 작성하던 중에 갑자기 무명 메소드가 필요해졌다고 생각해 보자.


무명 메소드를 사용하기 위해서는 이를 참조할 수 있는 델리게이트 변수가 있어야 하며,


또한 델리게이트 변수를 생성하기에 앞서 델리게이트 타입을 선언해야 한다.


그러면 각기 다른 타입의 무명 메소드를 여러개 만들 때는 어떻게 해야할까?


당연히 무명 메소드마다 그 타입에 맞는 델리게이트 타입과 변수를 따로 따로 선언해야 할 것이다.


이는 매우 비효율적인 작업이기 때문에 C#에서는 Func과 Action이라는 델리게이트를 제공한다.



Func와 Action은 미리 선언된 델리게이트 변수로써 별도의 선언없이 사용가능하다.


Func는 반환값이 있는 메소드를 참조하는 델리게이트 변수이고,


Action은 반환값이 없는 메소드를 참조하는 델리게이트 변수이다.




2. Func 델리게이트


.NET Framework에는 총 17가지의 Func 델리게이트가 준비되어 있다.


즉, 매개변수가 없는 메소드부터 매개변수가 16개인 메소드까지 


총 17개의 메소드를 참조 가능하다는 말이다. (무명 메소드 뿐만 아니라 일반 메소드도 참조 가능)


이정도면 특별한 경우가 아니고서야 별도의 델리게이트를 만들어 쓸 필요가 없겠다.


Func 델리게이트 변수를 선언하는 방법은 다음과 같다.




매개변수와 반환값을 구분하기 위해 int형과 float형으로 나누었다.


보시다시피 매개변수는 앞에서 지정하고, 반환값은 맨 뒤에 지정하는 것을 확인할 수 있다.

 

( Func는 반환값을 가진 메소드를 참조하는 델리게이트이기 때문에,


반환형을 반드시 지정해주어야 한다. )


위와 같이 Func 델리게이트로 메소드를 참조하면 전처럼 델리게이트 타입을 선언하는 과정이 


불필요해지므로 아주 간결하게 코드를 작성할 수 있다.



그럼 이제 다음 예제를 작성해보자.







3. Action 델리게이트


Action 델리게이트는 Func와 똑같다. 다만 참조하는 메소드의 반환값이 없을 뿐이다.




반환값이 없는것 빼고는 Func랑 똑같아서 딱히 설명이 필요 없겠다.


바로 아래 예제를 작성해보자.




ref : http://mrw0119.tistory.com/23







이벤트가 생겨난 이유는 속성이 생긴 이유와 비슷하다데이터 접근에 한 단계를 더 추가하여 캡슐화를 강화한다는 것에 있다다른 코드가 클래스의 필드 값을 최소한의 검증 작업도 없이 직접 수정하는 것이 바람직하지 않듯이 클래스 외부의 다른 코드가 특정 이벤트의 핸들러를 변경/호출 하는 것도 바람직하지 않다.  실제로 이벤트를 사용하면 컴파일러는 이 선언을 private 델리게이트 Type Filed로 변환하고 Add/Remove 기본 메서드를 구현해준다클래스 내부에서는 이것이 필드로 보이고 외부에서는 이벤트로 보이는 것이다.

 

 

 

 

실습을 통한 확인 

 

 아래는 이를 분석하는 간단한 코드를 작성하고 이에 대한 역어셈블 및 IL코드를 통해서 확인해보자. 

 

이벤트를 사용하는 클래스를 이용하여 이벤트 핸들러를 등록하고 다른 클래스에서 호출하는 예

 

using System;

 

namespace EventDasm

{

    delegate void EventDelegate();

 

class MyEventClass

    {

        public event EventDelegate Event;

 

        public void EventHandler()

        {

            Event();

        }

    }

 

 

    class Program

    {

        static void Main(string[] args)

        {

            MyEventClass EventInstance = new MyEventClass();

            EventInstance.Event += new EventDelegate(EventInstance_Event);

 

            EventInstance.EventHandler();

        }

 

        static void EventInstance_Event()

        {

            Console.WriteLine("이벤트 발생");

        }

    }

}

 

다음은 위 코드의 MyEventClass에 대한 역어셈블 결과 및 IL코드이다.

Disassemble

IL코드

보이는 것처럼 CLR에 의하여 자동적으로 private 델리게이트 타입 필드가 생성되고이에 대한add/remove메서드가 생성된 것을 확인할 수 있다.

 

 

지금까지 내용을 한 마디로 정리하면 다음과 같다

                                    “Event Delegate를 캡슐화 시킨 것이다.”





ref : https://silent1002.blog.me/10086120655

반응형
반응형

방법: 숫자 앞에 0으로 채우기

"D" 표준 숫자 서식 문자열과 함께 전체 자릿수 지정자를 사용하여 앞에 오는 0을 정수에 추가할 수 있습니다. 사용자 지정 숫자 서식 문자열을 사용하여 정수와 부동 소수점 숫자 둘 다에 앞에 오는 0을 추가할 수 있습니다. 이 항목에서는 두 개의 메서드를 사용하여 앞에 오는 0으로 숫자를 채우는 방법을 보여 줍니다.

앞에 오는 0으로 특정 길이까지 정수를 채우려면

  1. 표시할 정수 값의 최소 자릿수를 결정합니다. 이 숫자에 원하는 선행 자릿수를 포함합니다.

  2. 정수를 10진수 값으로 표시할지 아니면 16진수 값으로 표시할지를 결정합니다.

    • 호출 된 정수를 10 진수 값으로 표시 하려면 해당 ToString(String) 메서드와 패스 문자열 "Dn"의 값으로는 format 매개 변수를 여기서 n 문자열의 최소 길이 나타냅니다.

    • 호출 된 정수를 16 진수 값으로 표시 하려면 해당 ToString(String) 메서드와 전달 된 문자열 "Xn"의 값으로는 format 매개 변수를 여기서 n 문자열의 최소 길이 나타냅니다.

    메서드, 형식 문자열와 같은 사용할 수도 Format 또는 WriteLine를 사용 하 여 합성 서식 지정합니다.

다음 예제에서는 서식이 지정된 숫자의 전체 길이가 8자 이상이 되도록 앞에 오는 0으로 여러 정수 값의 서식을 지정합니다.

C#
byte byteValue = 254;
short shortValue = 10342;
int intValue = 1023983;
long lngValue = 6985321;               
ulong ulngValue = UInt64.MaxValue;

// Display integer values by calling the ToString method.
Console.WriteLine("{0,22} {1,22}", byteValue.ToString("D8"), byteValue.ToString("X8"));
Console.WriteLine("{0,22} {1,22}", shortValue.ToString("D8"), shortValue.ToString("X8"));
Console.WriteLine("{0,22} {1,22}", intValue.ToString("D8"), intValue.ToString("X8"));
Console.WriteLine("{0,22} {1,22}", lngValue.ToString("D8"), lngValue.ToString("X8"));
Console.WriteLine("{0,22} {1,22}", ulngValue.ToString("D8"), ulngValue.ToString("X8"));
Console.WriteLine();

// Display the same integer values by using composite formatting.
Console.WriteLine("{0,22:D8} {0,22:X8}", byteValue);
Console.WriteLine("{0,22:D8} {0,22:X8}", shortValue);
Console.WriteLine("{0,22:D8} {0,22:X8}", intValue);
Console.WriteLine("{0,22:D8} {0,22:X8}", lngValue);
Console.WriteLine("{0,22:D8} {0,22:X8}", ulngValue);
// The example displays the following output:
//                     00000254               000000FE
//                     00010342               00002866
//                     01023983               000F9FEF
//                     06985321               006A9669
//         18446744073709551615       FFFFFFFFFFFFFFFF
//       
//                     00000254               000000FE
//                     00010342               00002866
//                     01023983               000F9FEF
//                     06985321               006A9669
//         18446744073709551615       FFFFFFFFFFFFFFFF
//         18446744073709551615       FFFFFFFFFFFFFFFF

특정 수의 앞에 오는 0으로 정수를 채우려면

  1. 정수 값을 표시하는 데 사용할 앞에 오는 0의 수를 결정합니다.

  2. 정수를 10진수 값으로 표시할지 아니면 16진수 값으로 표시할지를 결정합니다. 10진수 값으로 서식을 지정하려면 "D" 표준 서식 지정자를 사용해야 하고, 16진수 값으로 서식을 지정하려면 "X" 표준 서식 지정자를 사용해야 합니다.

  3. 정수 값의 ToString("D").Length 또는 ToString("X").Length 메서드를 호출하여 채워지지 않은 숫자 문자열의 길이를 확인합니다.

  4. 서식이 지정된 문자열에 포함할 앞에 오는 0의 수를 채워지지 않은 숫자 문자열의 길이에 추가합니다. 그러면 채워진 문자열의 전체 길이가 정의됩니다.

  5. 정수 값의 호출 ToString(String) 메서드와 패스 문자열 "Dn" 10 진수 문자열 및 "Xn" 16 진수 문자열에 대 한 여기서 n 채워진된 문자열의 총 길이 나타냅니다. 사용할 수도 있습니다는 "Dn" 또는 "Xn" 복합 형식을 지 원하는 메서드의 문자열의 형식을 지정 합니다.

다음 예제에서는 5개의 앞에 오는 0으로 정수 값을 채웁니다.

C#
int value = 160934;
int decimalLength = value.ToString("D").Length + 5;
int hexLength = value.ToString("X").Length + 5;
Console.WriteLine(value.ToString("D" + decimalLength.ToString()));
Console.WriteLine(value.ToString("X" + hexLength.ToString()));
// The example displays the following output:
//       00000160934
//       00000274A6      

앞에 오는 0으로 특정 길이까지 숫자 값을 채우려면

  1. 숫자의 문자열 표현에서 정수 부분을 표시하는 데 사용할 자릿수를 결정합니다. 이 숫자의 전체 자릿수에 원하는 수의 앞에 오는 0을 포함합니다.

  2. 0 자리 표시자("0")를 사용하여 0의 최소 수를 나타내는 사용자 지정 숫자 서식 문자열을 정의합니다.

  3. 숫자의 ToString(String) 메서드를 호출하여 사용자 지정 서식 문자열에 전달합니다. 또한 합성 서식 지정을 지원하는 메서드에 사용자 지정 서식 문자열을 사용할 수 있습니다.

다음 예제에서는 서식이 지정된 숫자에서 정수 부분의 전체 길이가 8자 이상이 되도록 앞에 오는 0으로 여러 숫자 값의 서식을 지정합니다.

C#
string fmt = "00000000.##";
int intValue = 1053240;
decimal decValue = 103932.52m;
float sngValue = 1549230.10873992f;
double dblValue = 9034521202.93217412;

// Display the numbers using the ToString method.
Console.WriteLine(intValue.ToString(fmt));
Console.WriteLine(decValue.ToString(fmt));           
Console.WriteLine(sngValue.ToString(fmt));
Console.WriteLine(dblValue.ToString(fmt));           
Console.WriteLine();

// Display the numbers using composite formatting.
string formatString = " {0,15:" + fmt + "}";
Console.WriteLine(formatString, intValue);      
Console.WriteLine(formatString, decValue);      
Console.WriteLine(formatString, sngValue);      
Console.WriteLine(formatString, dblValue);      
// The example displays the following output:
//       01053240
//       00103932.52
//       01549230
//       9034521202.93
//       
//               01053240
//            00103932.52
//               01549230
//          9034521202.93      

특정 수의 앞에 오는 0으로 숫자 값을 채우려면

  1. 숫자 값을 표시하는 데 사용할 앞에 오는 0의 수를 결정합니다.

  2. 채워지지 않은 숫자 문자열에서 정수 부분의 자릿수를 확인합니다. 이렇게 하려면 다음을 수행합니다.

    1. 숫자의 문자열 표현에 소수점 기호가 포함되어 있는지 확인합니다.

    2. 소수점 기호가 포함되어 있으면 정수 부분에 있는 문자 수를 확인합니다.

      또는

      소수점 기호가 포함되어 있지 않으면 문자열의 길이를 확인합니다.

  3. 문자열에 표시할 각 앞에 오는 0에 대해 0 자리 표시자("0")를 사용하고 0 자리 표시자 또는 10진수 자리 표시자("#")를 사용하여 기본 문자열에 있는 각 자릿수를 나타내는 사용자 지정 서식 문자열을 만듭니다.

  4. 사용자 지정 서식 문자열을 숫자의 ToString(String) 메서드에 대한 매개 변수 또는 합성 서식 지정을 지원하는 메서드에 대한 매개 변수로 제공합니다.

다음 예제에서는 5개의 앞에 오는 0으로 두 개의 Double 값을 채웁니다.

C#
double[] dblValues = { 9034521202.93217412, 9034521202 };
foreach (double dblValue in dblValues)
{
   string decSeparator = System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
   string fmt, formatString;
   
   if (dblValue.ToString().Contains(decSeparator))
   {
      int digits = dblValue.ToString().IndexOf(decSeparator);
      fmt = new String('0', 5) + new String('#', digits) + ".##";
   }
   else
   {
      fmt = new String('0', dblValue.ToString().Length);   
   }
   formatString = "{0,20:" + fmt + "}";

   Console.WriteLine(dblValue.ToString(fmt));
   Console.WriteLine(formatString, dblValue);
}
// The example displays the following output:
//       000009034521202.93
//         000009034521202.93
//       9034521202
//                 9034521202            


반응형
반응형

우리는 전역(global) 변수와 정적(static) 변수를 혼용해서 쓰는 경우가 많다.

어느 정도 차이가 있다는것은 알지만 그 디테일에 대해서는 잘 모르는데,

이번 포스팅에서는 각 변수의 디테일한 특징과 차이에 대해 설명해 보도록 하자.







1. 전역 (global) 변수


라이프 타임은 프로그램이 죽을때까지이며, 다른 파일에서도 해당 변수에 접근이 가능하다.

scope가 없는 곳에서 "int g_value" 이런 식으로 선언한 변수는 전역 변수가 된다.

막상 쓰려고 할때 자주 헷갈리는 부분인데 이 변수를 다른 파일에서 사용하기 위해서는

"extern int g_value" 이런식으로 선언하고 사용해야한다. 이말은 int g_value가 다른 파일 어딘가에 있다는 것을 나타낸다.

정리하자면 본문에서는 "int g_value", 다른 파일에서는 "extern int g_value"이런 식으로 선언하여 사용해야 한다는것.








2. 정적 (static) 변수


라이프 타임은 전역변수와 마찬가지로 프로그램이 죽을때까지이다.

static이라는 키워드가 반드시 붙으며, 해당 변수가 선언된 scope에 따라서 접근 가능한 범위가 결정된다.

"static int s_value"

위와 같이 선언되며 다른 파일에서는 접근 할 수 없으며 오직 해당 파일에서만 scope에 맞게 접근이 가능하다.


흔히 비지역 정적 객체, 지역 정적 객체라는 헷갈리는 말을 많이 쓰는데, 이 말만 보면 비지역 정적 객체는 scope 없는 곳에서 만든 static int s_value같은 느낌이지만 그냥 지역적으로 만들어 지지 않은 전역 변수 + 정적 변수를 통칭한다. (Effective C++ 번역의 폐혜인듯..)








3. 전역 변수와 정적 변수의 메모리 할당 위치




위 메모리 맵을 보면 위쪽에 .data영역과 .bss영역이 있다.


.bss 영역은 초기화 되지 않은 전역변수 및 정적변수가 존재하는 공간으로 모든 값은 0으로 초기화되며

.data 영역은 초기화 된 전역변수 및 정적변수가 존재하는 공간이다. 하지만 수식이나 함수에 의해 초기화 된 변수일 경우, .data가 아닌 .bss 영역으로 들어가게 된다.



다음 각각의 경우를 통해 실제로 어떻게 메모리에 올라가는지 살펴보자



해당 코드를 컴파일하고, 어셈블리 코드를 까보면 다음과 같이 나오는걸 볼 수 있다.


보는것 처럼 초기화를 한 변수는 _DATA 영역에 메모리가 잡히며, 초기화 하지 않은 변수는 _BSS영역에 잡히는 것을 볼 수 있다.

하지만 static 변수의 경우, 그냥 global 변수와는 다르게 초기화를 하지 않은 상태에서 해당 변수를 본문 어디에서도 사용하지 않으면 메모리 자체가 잡히지 않는걸 확인 할 수 있었다.

반면 global 변수는 초기화를 하지 않더라도 무조건 메모리에 올라간다.

참고로 위 예제에서는 s_value2 변수를 본문에서 사용하고 있기때문에 메모리에 올라간 것이다.









4. 전역 변수와 정적 변수의 초기화 시점




① 정적 '객체'의 초기화

위 코드를 보면 static 변수와 static 객체를 지역적으로 선언하고 초기화 하고 있다.

그렇다면 초기화를 했기 때문에 두 값다 _DATA영역에 올라갈 것이고, 값의 초기화는 메모리에 올라감과 동시에 진행될 것인가?




어셈파일을 까본 결과 일반 변수의 대입 초기화 같은 경우 예상대로 _DATA영역에 올라가고 값도 초기화 되지만,

클래스 객체의 경우 _BSS영역에 올라가게 된다.

이렇게 _BSS영역에 올라간 객체는 대입이 이라면 dynamic initializer가 진행되고, 생성자를 통한 초기화라면 해당 변수가 처음 수행되는 시점에 초기화가 이루어진다. (dynamic initializer에 대해선 뒤에서..)





객체의 초기화를 디스어셈블로 지켜보면, 처음 해당 변수의 선언부가 수행되는 시점에 eax 레지스터의 값을 플레그로하여 값이 0이면 생성자 구문을 수행하고, 1이면 해당 생성자 코드는 점프해 버린다.


우리가 프로파일러를 만들때 static 객체를 이용하는 경우가 있는데, 저런식으로 scope안에 객체를 만들고 생성자를 통해 초기화를 진행하면 메모리는 처음부터 올라가 있지만 해당 생성자 구문은 처음 수행되는 시점에 한 번만 호출되는 것이다.

(루프문을 다시 돌아와 static Test class_value(10); 구문을 지나도 수행하지 않고 점프해 버린다는 뜻이다)





② dynamic initializer (동적 초기화)

그렇다면 위에서 말한 dynamic initializer란 무엇일까?

몇가지 예제 코드를 보자.


위 코드의 경우 컴파일 시점에서 functionValue라는 변수의 값을 알아 낼 수 없기 때문에 해당 변수는 _BSS영역에 메모리만 올리고(0으로도 초기화 될것이다) 추후에 동적으로 초기화가 수행된다.

main()함수가 실행되기 전에 해당 변수는 dynamic initializer를 수행하게 되고,

main 구문을 수행하고 있을때는 이미 10이란 값으로 초기화가 되어 있을것이다.









또다른 예를 보자.


다른 파일에 global 변수를 선언한다.



main 파일에서는 해당 변수를 extern으로 얻어다가,

static int s_value에 더한다. 이때 s_value값은 무엇으로 채워질것인가?



위 함수 예제와 마찬가지로 _BSS영역에 먼저 메모리를 올려 놓고 dynamic initializer를 수행한다.

다른 파일에 있는 global변수를 extern으로 선언하여 가져다가 사용할 때, 해당 변수의 값을 알수 있는 시점은 컴파일 시점이 아닌 링크 시점이다.


이를 번역단위라고 하는데, 각 파일은 각각의 번역단위라 할 수 있고 이 번역단위들은 링크시점에 하나의 실행파일로 묶이게 된다. 즉, 컴파일 시점에 알 수 없는 값이므로 임시로 메모리만 올려 놓고 dynamic initializer를 통해 main() 함수 전에 수행되는것이다. 


이와 관련해서 Effective C++에서는 다른 번역단위에서 사용하는 global 변수들의 초기화 순서가 정해져 있지 않음을 지적하고 있다.

번역단위가 여러개인 시점에서 위의 g_value1을 사용하는 다른 파일이 또 있고, g_value가 아직 링크되기 전이라면 0값으로 쓰여져 어떤 파일에서 g_value1 변수를 사용하는 부분이 있다면 문제의 소지가 생긴다는 뜻이다.


그래서 저자는 싱글톤처럼 초기화 시점을 조정할 수 있도록 만들기를 권고한다.(디테일은 생략)








5. 전역 변수와 정적 변수의 차이 정리


① 전역(global) 변수는 다른 파일에서도 가져다 쓸수 있지만 정적(static) 변수는 해당 파일의 scope안에서만 접근이 가능하다.


② 초기화 하지 않은 정적(static) 변수의 경우 본문에서 사용하지 않으면 아예 메모리 상에 올라오지 않는다.


③ 정적(static) 객체의 경우 처음 구문이 수행되는 시점에 처음 생성자를 호출하도록 할 수 있다.. 이를 함수화하여 호출을하면 생성자의 호출 시점을 조정하는게 가능해진다.




[참고]

https://www.tumblr.com/search/%EC%A0%95%EC%A0%81    // Effective C++ 잘 해설.

https://kldp.org/files/static____________160.htm               // static에 대한 설명

http://pangate.com/541                                       // extern과 static

https://kldp.org/node/122255                                 // bss의 용도

http://stackoverflow.com/questions/5945897/what-is-dynamic-intialization-of-object-in-c // 스택 오버플로우, 동적 초기화



ref : http://chfhrqnfrhc.tistory.com/entry/전역변수와-전적변수

반응형
반응형

VS 에서 작성한 풀소스  1~100  까지의 숫자 중 짝수만을 더한 구문을


C언어와 asm 으로 작성



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
#include <iostream>
 

int main()
{
//일반적인 C언어 더하기 구문
    int total = 0;
    int i = 0;
    for (int i=0;i<=100;i+=2)
    {
        total += i;
    }
 
 
    total = 0;
 

//어셈으로 작성한 더하기 구문
    __asm 
    {
        pushad
 
        mov eax, 0
 
        LP:
 
        add eax, 2
 
        add total, eax
 
        cmp eax , 100
        jnge LP
 
        popad
    }
 
    std::cout << total << std::endl;
 
 
    return 0;
}
 
cs





VS, C언어 작성하여 생성된 asm


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
int total = 0;
 
 mov         dword ptr [total],0  
 
    int i = 0;
 
 mov         dword ptr [i],0  
 
    for (int i=0;i<=100;i+=2)
 
 mov         dword ptr [ebp-24h],0  
 
 jmp         main+48h (0F91FA8h)  
 
 mov         eax,dword ptr [ebp-24h]  
 
 add         eax,2  
 
 mov         dword ptr [ebp-24h],eax  
 
 cmp         dword ptr [ebp-24h],64h  
 
 jg          main+59h (0F91FB9h)  
 
    {
 
        total += i;
 
 mov         eax,dword ptr [total]  
 
 add         eax,dword ptr [ebp-24h]  
 
 mov         dword ptr [total],eax  
 
    }
 
 jmp         main+3Fh (0F91F9Fh)  
 
 
cs




직접 작성한 asm

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
total = 0;
 
    __asm 
    {

        pushad

        mov eax, 0
 
        LP:

        add eax, 2
 
        add total, eax
 
        cmp eax , 100
 
        jnge LP
 
 
 
        popad
 
    }
cs




반응형
반응형


cmp 를 사용하게 되면 


cmp eax, 8 

의 경우 이 값이 같은지 비교해 보고 내부 플래그값을 세팅해놓는다


그 이후 


jp(jump) 명령어들 중에서 원하는 명령어를 써주면 LOOP : 레이블로 점프하게 된다



 


jne ( jumpe not equal) 같지 않으면 LOOP 로 간다음

다시 eax 를 1 증가 시킨다


if( i < 1000 )  조건이 성립할때까지 반복함   


즉 이 코드는 do while 문과 유사한 형태로 볼 수 있다





tip : eax 값을 초기에 10을 넣고 하나씩 감소 시키다가 eax 가 0 이 되는 순간 내부 zero 플래그의 상태 값이  1로 변경된다

이때 사용하는 점프문은 jz, jnz 등이 있다




반응형
반응형


레지스터에서 eax 가 존재하고 물리적으로는 edx 가 일자로 위 그림처럼 위치해 있는것

eax 에서 연산하다가 넘치면 edx 로 넘친다



여기서 우선 중요한 레지스터는 

EAX, EDX, ECX, EBX, ESI, EDI 이고 이것들은 연산을 하기위한 레지스터들과 

데이터 조작을 할때 ESI, EDI 등을 사용한다 (Source, Destination)

EBP, ESP 는 스택포인터들을 말한다





어셈 명령어중


PUSHAD  : Push All General-Purpose Registers

POPAD    : Pop All General-Purpose Registers

가 있는데 pushad 는 레지스터를 빽업(스택에 저장) 하는것이고 popad 는 빽업했던 레지스터 내용들을 다시 복구하는 작업을 한다



int a=10,b=10, c=0;

__asm

{

  pushad

   mov eax,, a

   mov ebx,, b

   add eax, ebx

   mov c, eax

  popad

}



ㅇㅀ

  1. 사용하던 레지스터 값들을 스택에 빽업

  2. 위의 어셈블리에서연산 처리과정
    LSU 를 통해서 Memory Data 에서 변수 값을 읽어와 eax, edx  레지스터에 값을 복사

  3.  명령에셋 Instruction fetch 에서 더하기 명령어를 가져와서 ALU 에 넣는다 

  4. eax와 edx에 있는 값과 명령에셋에서 가져온 더하기 명령어와 함께 ALU 를 통해서 연산한다

  5. 더하기 연산을 한다음 eax 에 누적한다

  6. LSU 를 통해서 결과 값을 변수 c(메모리 영역에 있는 변수) 에 대입

  7. 이전 레지스터 값 복구





반응형
반응형

__declspec(align(#))를 사용하여 사용자 정의 데이터(예: 함수의 정적 할당 또는 자동 데이터)를 정확하게 제어합니다.

__declspec( align( # ) ) declarator  

최신 프로세서 명령을 사용하는 응용 프로그램을 작성할 경우 몇 가지 새로운 제약 조건과 문제가 생깁니다. 특히 새로운 명령을 사용할 때 데이터를 16바이트 경계에 맞춰야 하는 경우가 많습니다. 또한 자주 사용하는 데이터를 특정 프로세서의 캐시 줄 크기에 맞춤으로써 캐시 성능이 향상됩니다. 예를 들어 크기가 32바이트 미만인 구조체를 정의할 경우 해당 구조체 형식의 개체가 효과적으로 캐시되도록 이 구조체를 32바이트에 맞출 수 있습니다.

#은 맞춤 값입니다. 유효한 항목은 2, 4, 8, 16, 32 또는 64와 같이 1에서 8192(바이트) 사이에 속하는 2의 정수 제곱입니다. declarator는 aligned로 선언하는 데이터입니다.

형식의 맞춤 요구 사항인 size_t 형식의 값을 반환하는 방법에 대한 자세한 내용은 __alignof를 참조하고 64비트 프로세서를 대상으로 하는 경우 정렬되지 않은 포인터를 선언하는 방법은 __unaligned를 참조하세요.

__declspec(align(#))struct 또는 union를 정의하거나 변수를 선언할 때 class를 사용할 수 있습니다.

컴파일러는 복사 또는 데이터 변환 작업 중에 데이터의 맞춤 특성 보존을 보장하거나 시도하지 않습니다. 예를 들어 memcpy는 __declspec(align(#))로 선언된 구조체를 임의의 위치에 복사할 수 있습니다. malloc, C++ operator new 및 Win32 할당자와 같은 일반 할당자가 반환하는 메모리는 대개 __declspec(align(#)) 구조체 또는 구조체 배열에 사용할 수 있도록 충분히 맞춰지지 않은 상태입니다. 복사 또는 데이터 변환 작업의 대상이 올바르게 맞춰지도록 하려면 _aligned_malloc를 사용하거나 할당자를 직접 작성합니다.

함수 매개 변수의 맞춤을 지정할 수 없습니다. 맞춤 특성을 포함하는 데이터가 스택의 값에 의해 전달되면 해당 맞춤은 호출 규칙을 통해 제어됩니다. 호출된 함수에서 데이터 맞춤이 중요한 경우에는 사용 전에 매개 변수를 올바르게 맞춰진 메모리로 복사합니다.

__declspec(align(#))를 사용하지 않는 경우 Visual C++는 일반적으로 대상 프로세서와 데이터 크기를 기반으로 자연 경계에 데이터를 맞춥니다(32비트 프로세서에서는 최대 4바이트 경계, 64비트 프로세서에서는 최대 8바이트 경계). 클래스 또는 구조체의 데이터는 최소한의 자연 맞춤 및 현재의 압축 설정(#pragma pack 또는 /Zp 컴파일러 옵션에서)으로 클래스나 구조체에 맞춰집니다.

이 예제에서는 __declspec(align(#))의 사용을 보여 줍니다.

__declspec(align(32)) struct Str1{  
   int a, b, c, d, e;  
};  

현재 이 형식에는 32비트 맞춤 특성이 포함되어 있습니다. 즉, 모든 정적 및 자동 인스턴스가 32바이트 경계에서 시작됩니다. 이 형식을 사용하여 멤버로 선언된 추가 구조체 형식은 이 형식의 맞춤 특성을 유지하여 Str1이 요소로 지정된 모든 구조체는 32 이상의 맞춤 특성을 갖게 됩니다.

sizeof(struct Str1)는 32입니다. 즉, Str1 개체 배열을 만드는 경우 배열의 기준을 32바이트로 맞추면 배열의 각 멤버도 32바이트로 맞춰집니다. 동적 메모리에서 기준이 올바르게 맞춰진 배열을 만들려면 _aligned_malloc를 사용하거나 할당자를 직접 작성합니다.

구조체의 sizeof 값은 최종 멤버의 오프셋에 해당 멤버의 크기를 더하여 최대 멤버 맞춤 값 또는 전체 구조체 맞춤 값 중 더 큰 값의 가장 근사한 배수로 반올림한 값입니다.

컴파일러는 구조체 맞춤에 다음과 같은 규칙을 사용합니다.

  • __declspec(align(#))로 재정의하지 않으면 스칼라 구조체 멤버의 맞춤은 최소 크기와 현재 압축입니다.

  • __declspec(align(#))로 재정의하지 않으면 구조체의 멤버는 멤버의 최대 개별 맞춤입니다.

  • 구조체 멤버는 이전 멤버 끝의 오프셋보다 크거나 같은 맞춤의 최소 배수인 부모 구조체의 시작 부분부터 오프셋에 배치됩니다.

  • 구조체의 크기는 마지막 멤버의 오프셋 끝보다 크거나 같은 맞춤의 최소 배수입니다.

__declspec(align(#))는 맞춤 제한만 늘릴 수 있습니다.




맞춤 (C++)

Visual Studio 2015
 

Visual Studio 2017 에 대한 최신 설명서는 Visual Studio 2017 설명서를 참조하세요.

Visual Studio 2015 이상 버전에서는 C++11 표준 alignas 지정자를 사용하여 맞춤을 제어합니다. 자세한 내용은 맞춤을 참조하세요.

Microsoft 전용

__declspec(align(#))를 사용하여 사용자 정의 데이터(예: 함수의 정적 할당 또는 자동 데이터)를 정확하게 제어합니다.

__declspec( align( # ) ) declarator  

최신 프로세서 명령을 사용하는 응용 프로그램을 작성할 경우 몇 가지 새로운 제약 조건과 문제가 생깁니다. 특히 새로운 명령을 사용할 때 데이터를 16바이트 경계에 맞춰야 하는 경우가 많습니다. 또한 자주 사용하는 데이터를 특정 프로세서의 캐시 줄 크기에 맞춤으로써 캐시 성능이 향상됩니다. 예를 들어 크기가 32바이트 미만인 구조체를 정의할 경우 해당 구조체 형식의 개체가 효과적으로 캐시되도록 이 구조체를 32바이트에 맞출 수 있습니다.

#은 맞춤 값입니다. 유효한 항목은 2, 4, 8, 16, 32 또는 64와 같이 1에서 8192(바이트) 사이에 속하는 2의 정수 제곱입니다. declarator는 aligned로 선언하는 데이터입니다.

형식의 맞춤 요구 사항인 size_t 형식의 값을 반환하는 방법에 대한 자세한 내용은 __alignof를 참조하고 64비트 프로세서를 대상으로 하는 경우 정렬되지 않은 포인터를 선언하는 방법은 __unaligned를 참조하세요.

__declspec(align(#))struct 또는 union를 정의하거나 변수를 선언할 때 class를 사용할 수 있습니다.

컴파일러는 복사 또는 데이터 변환 작업 중에 데이터의 맞춤 특성 보존을 보장하거나 시도하지 않습니다. 예를 들어 memcpy는 __declspec(align(#))로 선언된 구조체를 임의의 위치에 복사할 수 있습니다. malloc, C++ operator new 및 Win32 할당자와 같은 일반 할당자가 반환하는 메모리는 대개 __declspec(align(#)) 구조체 또는 구조체 배열에 사용할 수 있도록 충분히 맞춰지지 않은 상태입니다. 복사 또는 데이터 변환 작업의 대상이 올바르게 맞춰지도록 하려면 _aligned_malloc를 사용하거나 할당자를 직접 작성합니다.

함수 매개 변수의 맞춤을 지정할 수 없습니다. 맞춤 특성을 포함하는 데이터가 스택의 값에 의해 전달되면 해당 맞춤은 호출 규칙을 통해 제어됩니다. 호출된 함수에서 데이터 맞춤이 중요한 경우에는 사용 전에 매개 변수를 올바르게 맞춰진 메모리로 복사합니다.

__declspec(align(#))를 사용하지 않는 경우 Visual C++는 일반적으로 대상 프로세서와 데이터 크기를 기반으로 자연 경계에 데이터를 맞춥니다(32비트 프로세서에서는 최대 4바이트 경계, 64비트 프로세서에서는 최대 8바이트 경계). 클래스 또는 구조체의 데이터는 최소한의 자연 맞춤 및 현재의 압축 설정(#pragma pack 또는 /Zp 컴파일러 옵션에서)으로 클래스나 구조체에 맞춰집니다.

이 예제에서는 __declspec(align(#))의 사용을 보여 줍니다.

__declspec(align(32)) struct Str1{  
   int a, b, c, d, e;  
};  

현재 이 형식에는 32비트 맞춤 특성이 포함되어 있습니다. 즉, 모든 정적 및 자동 인스턴스가 32바이트 경계에서 시작됩니다. 이 형식을 사용하여 멤버로 선언된 추가 구조체 형식은 이 형식의 맞춤 특성을 유지하여 Str1이 요소로 지정된 모든 구조체는 32 이상의 맞춤 특성을 갖게 됩니다.

sizeof(struct Str1)는 32입니다. 즉, Str1 개체 배열을 만드는 경우 배열의 기준을 32바이트로 맞추면 배열의 각 멤버도 32바이트로 맞춰집니다. 동적 메모리에서 기준이 올바르게 맞춰진 배열을 만들려면 _aligned_malloc를 사용하거나 할당자를 직접 작성합니다.

구조체의 sizeof 값은 최종 멤버의 오프셋에 해당 멤버의 크기를 더하여 최대 멤버 맞춤 값 또는 전체 구조체 맞춤 값 중 더 큰 값의 가장 근사한 배수로 반올림한 값입니다.

컴파일러는 구조체 맞춤에 다음과 같은 규칙을 사용합니다.

  • __declspec(align(#))로 재정의하지 않으면 스칼라 구조체 멤버의 맞춤은 최소 크기와 현재 압축입니다.

  • __declspec(align(#))로 재정의하지 않으면 구조체의 멤버는 멤버의 최대 개별 맞춤입니다.

  • 구조체 멤버는 이전 멤버 끝의 오프셋보다 크거나 같은 맞춤의 최소 배수인 부모 구조체의 시작 부분부터 오프셋에 배치됩니다.

  • 구조체의 크기는 마지막 멤버의 오프셋 끝보다 크거나 같은 맞춤의 최소 배수입니다.

__declspec(align(#))는 맞춤 제한만 늘릴 수 있습니다.

자세한 내용은 다음을 참조하세요.

다음 예제에서는 __declspec(align(#))가 데이터 구조체의 크기 및 맞춤에 영향을 주는 방식을 보여 줍니다. 예제에서는 다음과 같은 정의를 가정합니다

#define CACHE_LINE  32  
#define CACHE_ALIGN __declspec(align(CACHE_LINE))  



이 예제에서는 S1를 사용하여 __declspec(align(32)) 구조체를 정의합니다. 변수 정의에 대해 또는 기타 형식 선언에서 사용되는 모든 S1은 32바이트로 맞춰집니다. sizeof(struct S1)는 32를 반환하고 S1은 16바이트 뒤에 정수 4개에 필요한 16 패딩 바이트를 둡니다. 각 int 멤버는 4바이트로 맞춰야 하지만 구조체 자체의 맞춤은 32로 선언됩니다. 따라서 전체 맞춤은 32입니다.

struct CACHE_ALIGN S1 { // cache align all instances of S1  
   int a, b, c, d;  
};  
struct S1 s1;   // s1 is 32-byte cache aligned  



이 예제에서는 최대 맞춤 요구 사항의 배수가 8의 배수인 16이므로 sizeof(struct S2)는 멤버 크기의 합계와 똑같은 16을 반환합니다.

__declspec(align(8)) struct S2 {  
   int a, b, c, d;  
};  



다음 예제에서 sizeof(struct S3)는 64를 반환합니다.

struct S3 {  
   struct S1 s1;   // S3 inherits cache alignment requirement  
                  // from S1 declaration  
   int a;         // a is now cache aligned because of s1  
                  // 28 bytes of trailing padding  
};  



이 예제에서 a에는 자연 형식 맞춤(여기서는 4바이트)이 사용됩니다. 그러나 S1은 32바이트 맞춤이어야 합니다. 28바이트 패딩이 a 뒤에 나와 s1이 32 오프셋에서 시작됩니다. 그런 다음 S4가 구조체의 최대 맞춤 요구 사항인 S1의 맞춤 요구 사항을 상속합니다. sizeof(struct S4)가 64를 반환합니다.

struct S4 {  
   int a;  
   // 28 bytes padding  
    struct S1 s1;      // S4 inherits cache alignment requirement of S1  
};  








다음 3개의 변수 선언에도 __declspec(align(#))가 사용됩니다. 각 선언에서 변수가 32바이트 맞춤이어야 합니다. 배열의 경우 각 배열 멤버가 아니라 배열의 기준 주소가 32바이트 맞춤입니다. sizeof를 사용해도 각 배열 멤버의 __declspec(align(#)) 값에 영향을 주지 않습니다.

CACHE_ALIGN int i; CACHE_ALIGN int array[128]; //배열의 경우에 32 바이트단위로 맞춤(끊어)정렬된다 CACHE_ALIGN struct s2 s;




배열의 각 멤버를 맞추려면 다음과 같은 코드를 사용해야 합니다.

typedef CACHE_ALIGN struct { int a; } S5;  
S5 array[10];  


반응형
반응형
using System;

public class Example
{
   public static void Main()
   {
      string[] values = { "+13230", "-0", "1,390,146", "$190,235,421,127",
                          "0xFA1B", "163042", "-10", "007", "2147483647", 
                          "2147483648", "16e07", "134985.0", "-12034",
                          "-2147483648", "-2147483649" };
      foreach (string value in values)
      {
         try {
            int number = Int32.Parse(value); 
            Console.WriteLine("{0} --> {1}", value, number);
         }
         catch (FormatException) {
            Console.WriteLine("{0}: Bad Format", value);
         }   
         catch (OverflowException) {
            Console.WriteLine("{0}: Overflow", value);   
         }  
      }
   }
}
// The example displays the following output:
//       +13230 --> 13230
//       -0 --> 0
//       1,390,146: Bad Format
//       $190,235,421,127: Bad Format
//       0xFA1B: Bad Format
//       163042 --> 163042
//       -10 --> -10
//       007 --> 7
//       2147483647 --> 2147483647
//       2147483648: Overflow
//       16e07: Bad Format
//       134985.0: Bad Format
//       -12034 --> -12034
//       -2147483648 --> -2147483648
//       -2147483649: Overflow      


반응형
반응형


IEnumerable 에서 다뤄지는 요소들이 꼭 배열일 필요는 없다, 상황에 맞는 구조를 사용하면 됨



IEnumerator모든 제네릭이 아닌 열거자에 대 한 기본 인터페이스입니다.

이 인터페이스의 제네릭 버전에 대 한 참조 IEnumerator<T>합니다.

@FSHO1@C# 언어의 foreach 문(Visual Basic의 경우 for each)은 열거자의 복잡성을 숨깁니다. 따라서 사용 하 여 foreach 열거자를 직접 조작 하는 대신 것이 좋습니다.

열거자를 사용하여 컬렉션의 데이터를 읽을 수는 있지만 내부 컬렉션을 수정할 수는 없습니다.

Reset 메서드는 COM 상호 운용성을 위해 제공 되며 완벽 하 게 구현 하지 않아도; 구현자 대신 throw 할 수는 NotSupportedException합니다.

처음에 열거자는 컬렉션의 첫 번째 요소 앞에 배치됩니다. 호출 해야 합니다는 MoveNext 하 여 열거자의 값을 읽기 전에 컬렉션의 첫 번째 요소로 메서드 Current, 그렇지 않으면 Current 정의 되지 않습니다.

Current에서는 MoveNext 또는 Reset이 호출될 때까지 동일한 개체를 반환합니다. MoveNext는 Current를 다음 요소로 설정합니다.

경우 MoveNext 전달 컬렉션의 마지막 요소 뒤에 열거자는 컬렉션의 끝에 배치 되 고 MoveNext 반환 false합니다. 열거자가 있는 경우이 위치에 대 한 후속 호출에서 MoveNext 반환할 수도 false합니다. 마지막으로 호출 하는 경우 MoveNext 반환 false호출, Current 예외를 throw 합니다.

설정 하려면 Current 다시 컬렉션의 첫 번째 요소를 호출할 수 있습니다 Reset구현 되는 경우, 다음 MoveNext합니다. 경우 Reset 는 구현 하지 않은 만들어야 새 열거자 인스턴스를 컬렉션의 첫 번째 요소를 반환 합니다.

열거자는 컬렉션이 변경되지 않은 상태로 유지되는 한 유효한 상태를 유지합니다. 변경, 추가 하는 등 컬렉션을 수정 하거나 요소를 삭제 하면 열거자가 더 유효 하지 않으며을 다음에 호출할 MoveNext 또는 Reset throw는 InvalidOperationException합니다. 사이 컬렉션이 수정 되는 경우 MoveNext 및 CurrentCurrent 열거자가 이미 무효로 되 었 하는 경우에 설정 하는 요소를 반환 합니다.

열거자는 컬렉션에 배타적으로 액세스하지 못하므로 컬렉션을 열거하는 것은 본질적으로 스레드로부터 안전한 프로시저가 아닙니다. 컬렉션이 동기화되어 있을 때 다른 스레드에서 해당 컬렉션을 수정할 수 있으므로 이렇게 되면 열거자에서 예외가 throw됩니다. 열거하는 동안 스레드로부터 안전을 보장하려면 전체 열거를 수행하는 동안 컬렉션을 잠그거나 다른 스레드에서 변경된 내용으로 인해 발생한 예외를 catch하면 됩니다.



다음 코드 예제에서는의 구현을 설명는 IEnumerable 및 IEnumerator 사용자 지정 컬렉션에 대 한 인터페이스입니다. 이 예제에서는 이러한 인터페이스의 멤버는 명시적으로 호출 되지 않지만의 사용을 지원 하도록 구현 됩니다 foreach (for each Visual basic에서)는 컬렉션을 반복 합니다.



using System;
using System.Collections;

// Simple business object.
public class Person
{
    public Person(string fName, string lName)
    {
        this.firstName = fName;
        this.lastName = lName;
    }

    public string firstName;
    public string lastName;
}

// Collection of Person objects. This class
// implements IEnumerable so that it can be used
// with ForEach syntax.
public class People : IEnumerable
{
    private Person[] _people;
    public People(Person[] pArray)
    {
        _people = new Person[pArray.Length];

        for (int i = 0; i < pArray.Length; i++)
        {
            _people[i] = pArray[i];
        }
    }

// Implementation for the GetEnumerator method.
    IEnumerator IEnumerable.GetEnumerator()
    {
       return (IEnumerator) GetEnumerator();
    }

    public PeopleEnum GetEnumerator()
    {
        return new PeopleEnum(_people);
    }
}

// When you implement IEnumerable, you must also implement IEnumerator.
public class PeopleEnum : IEnumerator
{
    public Person[] _people;

    // Enumerators are positioned before the first element
    // until the first MoveNext() call.
    int position = -1;

    public PeopleEnum(Person[] list)
    {
        _people = list;
    }

    public bool MoveNext()
    {
        position++;
        return (position < _people.Length);
    }

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

    object IEnumerator.Current
    {
        get
        {
            return Current;
        }
    }

    public Person Current
    {
        get
        {
            try
            {
                return _people[position];
            }
            catch (IndexOutOfRangeException)
            {
                throw new InvalidOperationException();
            }
        }
    }
}

class App
{
    static void Main()
    {
        Person[] peopleArray = new Person[3]
        {
            new Person("John", "Smith"),
            new Person("Jim", "Johnson"),
            new Person("Sue", "Rabon"),
        };

        People peopleList = new People(peopleArray);
        foreach (Person p in peopleList)
            Console.WriteLine(p.firstName + " " + p.lastName);

    }
}

/* This code produces output similar to the following:
 *
 * John Smith
 * Jim Johnson
 * Sue Rabon
 *
 */



ref : https://msdn.microsoft.com/ko-kr/library/system.collections.ienumerator(v=vs.110).aspx


반응형
반응형



C# 3.0 이상에서는 속성 접근자에 추가적인 논리가 필요하지 않을 경우 자동 구현 속성을 통해 속성 선언이 더 간결해집니다. 이를 통해 클라이언트 코드에서 개체를 만들 수도 있습니다. 다음 예제와 같이 속성을 선언할 때 컴파일러는
속성의 get 및 set 접근자를 통해서만 액세스할 수 있는 전용 익명 지원 필드를 만듭니다.

예제

다음 예제에서는 일부 자동 구현 속성이 있는 간단한 클래스를 보여 줍니다.

C#

// This class is mutable. Its data can be modified from
// outside the class.
class Customer
{
    // Auto-Impl Properties for trivial get and set
    public double TotalPurchases { get; set; }
    public string Name { get; set; }
    public int CustomerID { get; set; }

    // Constructor
    public Customer(double purchases, string name, int ID)
    {
        TotalPurchases = purchases;
        Name = name;
        CustomerID = ID;
    }
    // Methods
    public string GetContactInfo() {return "ContactInfo";}
    public string GetTransactionHistory() {return "History";}

    // .. Additional methods, events, etc.
}

class Program
{
    static void Main()
    {
        // Intialize a new object.
        Customer cust1 = new Customer ( 4987.63, "Northwind",90108 );

        //Modify a property
        cust1.TotalPurchases += 499.99;
    }
}

C# 6 이상 버전에서는 필드와 유사하게 자동 구현 속성을 초기화할 수 있습니다.

C#
public string FirstName { get; set; } = "Jane";  

앞의 예제에 표시된 클래스는 변경할 수 있습니다. 클라이언트 코드에서는 개체가 만들어진 후 개체의 값을 변경할 수 있습니다. 데이터 및 중요 동작(메서드)을 포함하는 복잡한 클래스에는 공용 속성을 포함해야 할 수 있습니다. 그러나 값 집합(데이터)만 캡슐화하고 동작이 적거나 없는 작은 클래스나 구조체의 경우 set 접근자를 private로 선언하거나(소비자에 대한 변경 불가능) get 접근자만 선언하여(생성자를 제외한 모든 위치에서 변경 불가능) 개체를 변경 불가능으로 설정해야 합니다. 자세한 내용은 방법: 자동으로 구현된 속성을 사용하여 간단한 클래스 구현을 참조하세요.


특성은 자동 구현 속성에서 허용되지만, 소스 코드에서 액세스할 수 없으므로 지원 필드에서는 분명히 허용되지 않습니다. 속성의 지원 필드에서 특성을 사용해야 할 경우 일반 속성만 만듭니다.


https://docs.microsoft.com/ko-kr/dotnet/csharp/programming-guide/classes-and-structs/auto-implemented-properties



반응형
반응형
Nullable 타입의 도입 

C#에서 정수, 부동자릿수, 구조체 등의 Value Type은 NULL을 가질 수 없다. 예를 들어, 정수 int i 가 있을 때 변수 i에는 null을 할당할 수 없으며, 따라서 변수 i는 어떤 값이 할당되지 않은 상태 (missing value)를 가질 수 없다.
만약 정수형 변수 i에 값이 설정되지 않은 상태를 할당하려면, 개발자는 2가지 방법을 사용할 수 있을 것이다. 즉, (1)프로그램에서 사용될 것 같지 않은 특정 값을 추정하여 할당하던지 (예를 들어, int i = int.MaxValue;) (2) 아니면 또 하나의 변수를 두어 변수 i가 missing임을 나타내게 할 수 있다 (예를 들어, bool iHasValue = false;). 이 두번째 방식이 Nullable의 기본 아이디어이다.

C# 에서는 Value Type에도 null을 할당할 수 있는 Nullable 타입을 지원한다. Nullable 타입은 Value 값을 갖고 있으면서 NULL 상태를 체크할 수 있는 기능(HasValue)을 함께 가지고 있는 struct 이다. 따라서 Nullable 타입은 struct(구조체)이므로 Value Type이다.

C#에서 int? 와 같이 해당 Value Type 뒤에 물음표를 붙이면, 해당 정수형 타입이 Nullable 정수형 타입임을 의미한다. 즉, 이 변수에는 NULL을 할당할 수 있다. C#의 이러한 특별한 문법은 .NET의 Nullable<T> 구조체로 컴파일시 변환된다. 즉, int?는 Nullable<int>와 동일하다. 





Nullable<T> 타입 

C#에서 int?, bool?, DateTime? 와 같은 T? 의 표현은 .NET의 Nullable<T>와 같은 표현이며, Nullable<T> 구조체는 값을 가지고 있는지를 체크하는 (즉, missing value가 아닌지를 체크하는) HasValue 속성과 실제 값을 나타내는 Value 속성을 가지고 있다. Nullable 구조체는 타입 캐스팅을 통한 변환이나 암묵적 변환을 통해 (Non-nullable) Value 타입으로 변환된다. 당연한 얘기지만, Value 타입이 아닌 레퍼런스 타입은 Nullable을 쓸 필요가 없는데, 그것은 모든 레퍼런스 타입은 이미 NULL을 허용하기 때문이다.
Nullable 타입이 실무에서 흔히 사용되는 케이스는 DB와 연동되는 Data Layer 클래스들을 들 수 있는데, 예를 들어 SQL 서버 테이블에서 NULL을 허용하는 숫자, 날짜, bool등의 컬럼이 있다면, 이 컬럼 타입은 Nullable 타입으로 변환되어 테이블의 NULL 속성을 표현하게 된다. 아래 예제는 다양한 종류의 Nullable 파라미터를 받아들여 HasValue로 NULL 값을 미리 체크한 후, .Value 속성을 써서 해당 타입의 실제 값을 사용하고 있다.

Nullable 타입과 연관되어 자주 사용되는 C#의 ?? 연산자는 ?? 앞의 파라미터가 NULL인 경우 연산자 뒤의 값을 할당할 때 사용한다. 


예제

double _Sum = 0;
DateTime _Time;
bool? _Selected;

public void CheckInput(int? i, double? d, DateTime? time, bool? selected)
{
    if (i.HasValue && d.HasValue)
        this._Sum = (double)i.Value + (double)d.Value;

    // time값이 있는 체크.
    if (!time.HasValue)
        throw new ArgumentException();
    else
        this._Time = time.Value;

    // 만약 selected가 NULL이면 false를 할당
    this._Selected = selected ?? false;
}



Nullable 정적 클래스 

.NET Framework에 있는 정적(static) 클래스 System.Nullable 은 두개의 Nullable 객체를 비교하거나 (Compare(), Equals() 메서드), 특정 Nullable 타입이 어떤 Value 타입에 기반을 두고 있는지 알아내는 (GetUnderlyingType() 메서드) 기능을 제공하고 있다. 이 정적 클래스는 Nullable<T> 타입을 위한 몇 가지 편리한 정적 메서드들을 제공한다. 

예제

void NullableTest()
{
    int? a = null;
    int? b = 0;            
    int result = Nullable.Compare<int>(a, b);
    Console.WriteLine(result); //결과 -1

    double? c = 0.01;
    double? d = 0.0100;
    bool result2 = Nullable.Equals<double>(c, d);
    Console.WriteLine(result2); //결과 true
}



http://www.csharpstudy.com/CSharp/CSharp-nullable.aspx



반응형

+ Recent posts