C# 개념 (1)-네임스페이스,튜플,형식분해

C# 개념 (1)-네임스페이스,튜플,형식분해

네임스페이스(namespace)

1
System.Console.WriteLine("Hello World!");

위 코드에서 System은 네임스페이스고 Console은 네임스페이스에 클래스입니다.

1
2
3
4
using System;

Console.WriteLine("Hello World!");

위처럼 using 키워드를 사용해서 사용할수도 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
namespace SampleNamespace
{
class SampleClass
{
public void SampleMethod()
{
System.Console.WriteLine(
"SampleMethod inside SampleNamespace");
}
}
}

고유한 네임스페이스를 선언하면 대규모 프로그래밍 프로젝트에서 클래스 및 메서드 이름에 범위를 지정할수 있습니다.

튜플(tuples) 및 형식분해(Deconstructing)

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 Example
{
public static void Main()
{
var result = QueryCityData("New York City");

var city = result.Item1;
var pop = result.Item2;
var size = result.Item3;

// Do something with the data.
}

private static (string, int, double) QueryCityData(string name)
{
if (name == "New York City")
return (name, 8175133, 468.48);

return ("", 0, 0);
}
}

위처럼 QueryCityData 메서드는 3 튜플을 반환하며 별도에 작업을 해야 변수에 할당됩니다.

튜플 분해

1
2
3
4
5
6
7
public static void Main()
{
(string city, int population, double area) = QueryCityData("New York City");

// Do something with the data.
}

위처럼 직접적으로 변수 타입을 지정할수도 있고

1
2
3
4
5
6
7
public static void Main()
{
var (city, population, area) = QueryCityData("New York City");

// Do something with the data.
}

타입을 유추할수 있게 처리 할수도 있게 할수도 있고

1
2
3
4
5
6
7
public static void Main()
{
(string city, var population, var area) = QueryCityData("New York City");

// Do something with the data.
}

개별적으로 var 키워드를 쓸수도 있다.

마지막으로는 기존에 있는 변수를 활용할수도 있다.

1
2
3
4
5
6
7
8
9
10
11
public static void Main()
{
string city = "Raleigh";
int population = 458880;
double area = 144.8;

(city, population, area) = QueryCityData("New York City");

// Do something with the data.
}

무시 항목을 사용한 튜플 분해

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;
using System.Collections.Generic;

public class Example
{
public static void Main()
{
var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");
}

private static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
{
int population1 = 0, population2 = 0;
double area = 0;

if (name == "New York City")
{
area = 468.48;
if (year1 == 1960)
{
population1 = 7781984;
}
if (year2 == 2010)
{
population2 = 8175133;
}
return (name, area, year1, population1, year2, population2);
}

return ("", 0, 0, 0, 0, 0);
}
}
// The example displays the following output:
// Population change, 1960 to 2010: 393,149

사용자 정의 형식 분해

c#은 튜플이 아닌 형식의 분해는 기본적으로 제공하지 않습니다.
그러나 클래스, 구조체 또는 인터페이스를 만든 이는 하나이상의 Deconstruct 메소드를 구현하여 분해할수 있습니다. out 시그니처를 활용

1
2
public void Deconstruct(out string fname, out string mname, out string lname)

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;

public class Person
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string City { get; set; }
public string State { get; set; }

public Person(string fname, string mname, string lname,
string cityName, string stateName)
{
FirstName = fname;
MiddleName = mname;
LastName = lname;
City = cityName;
State = stateName;
}

// Return the first and last name.
public void Deconstruct(out string fname, out string lname)
{
fname = FirstName;
lname = LastName;
}

public void Deconstruct(out string fname, out string mname, out string lname)
{
fname = FirstName;
mname = MiddleName;
lname = LastName;
}

public void Deconstruct(out string fname, out string lname,
out string city, out string state)
{
fname = FirstName;
lname = LastName;
city = City;
state = State;
}
}

public class Example
{
public static void Main()
{
var p = new Person("John", "Quincy", "Adams", "Boston", "MA");

// Deconstruct the person object.
var (fName, lName, city, state) = p;
Console.WriteLine($"Hello {fName} {lName} of {city}, {state}!");
}
}
// The example displays the following output:
// Hello John Adams of Boston, MA!

위에 예제에서는 다양하게 Deconstruct를 구현하여 분해를 할수 있습니다.

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
using System;

public class Person
{
public string FirstName { get; set; }
public string MiddleName { get; set; }
public string LastName { get; set; }
public string City { get; set; }
public string State { get; set; }
public DateTime DateOfBirth { get; set; }
public Decimal AnnualIncome { get; set; }

public void Deconstruct(out string fname, out string mname, out string lname, out int age)
{
fname = FirstName;
mname = MiddleName;
lname = LastName;

// calculate the person's age
var today = DateTime.Today;
age = today.Year - DateOfBirth.Year;
if (DateOfBirth.Date > today.AddYears(-age))
age--;
}

public void Deconstruct(out string lname, out string fname, out string mname, out decimal income)
{
fname = FirstName;
mname = MiddleName;
lname = LastName;
income = AnnualIncome;
}
}

위에 코드는 변수의 순서가 틀려서 혼동이 올수 있는 케이스입니다. 이렇게 활용하면 안되겠습니다.

무시항목을 사용한 사용자 정의 형식분해

1
2
3
4
5
6
7

// Deconstruct the person object.
var (fName, _, city, _) = p;
Console.WriteLine($"Hello {fName} of {city}!");
// The example displays the following output:
// Hello John of Boston!

확장 메서드를 사용한 사용자 정의 형식분해

System.Reflection.PropertyInfo 클래스에서 정의한 확장 메서드 구현이다

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

using System;
using System.Collections.Generic;
using System.Reflection;

public static class ReflectionExtensions
{
public static void Deconstruct(this PropertyInfo p, out bool isStatic,
out bool isReadOnly, out bool isIndexed,
out Type propertyType)
{
var getter = p.GetMethod;

// Is the property read-only?
isReadOnly = ! p.CanWrite;

// Is the property instance or static?
isStatic = getter.IsStatic;

// Is the property indexed?
isIndexed = p.GetIndexParameters().Length > 0;

// Get the property type.
propertyType = p.PropertyType;
}

public static void Deconstruct(this PropertyInfo p, out bool hasGetAndSet,
out bool sameAccess, out string access,
out string getAccess, out string setAccess)
{
hasGetAndSet = sameAccess = false;
string getAccessTemp = null;
string setAccessTemp = null;

MethodInfo getter = null;
if (p.CanRead)
getter = p.GetMethod;

MethodInfo setter = null;
if (p.CanWrite)
setter = p.SetMethod;

if (setter != null && getter != null)
hasGetAndSet = true;

if (getter != null)
{
if (getter.IsPublic)
getAccessTemp = "public";
else if (getter.IsPrivate)
getAccessTemp = "private";
else if (getter.IsAssembly)
getAccessTemp = "internal";
else if (getter.IsFamily)
getAccessTemp = "protected";
else if (getter.IsFamilyOrAssembly)
getAccessTemp = "protected internal";
}

if (setter != null)
{
if (setter.IsPublic)
setAccessTemp = "public";
else if (setter.IsPrivate)
setAccessTemp = "private";
else if (setter.IsAssembly)
setAccessTemp = "internal";
else if (setter.IsFamily)
setAccessTemp = "protected";
else if (setter.IsFamilyOrAssembly)
setAccessTemp = "protected internal";
}

// Are the accessibility of the getter and setter the same?
if (setAccessTemp == getAccessTemp)
{
sameAccess = true;
access = getAccessTemp;
getAccess = setAccess = String.Empty;
}
else
{
access = null;
getAccess = getAccessTemp;
setAccess = setAccessTemp;
}
}
}

public class Example
{
public static void Main()
{
Type dateType = typeof(DateTime);
PropertyInfo prop = dateType.GetProperty("Now");
var (isStatic, isRO, isIndexed, propType) = prop;
Console.WriteLine($"\nThe {dateType.FullName}.{prop.Name} property:");
Console.WriteLine($" PropertyType: {propType.Name}");
Console.WriteLine($" Static: {isStatic}");
Console.WriteLine($" Read-only: {isRO}");
Console.WriteLine($" Indexed: {isIndexed}");

Type listType = typeof(List<>);
prop = listType.GetProperty("Item",
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
var (hasGetAndSet, sameAccess, accessibility, getAccessibility, setAccessibility) = prop;
Console.Write($"\nAccessibility of the {listType.FullName}.{prop.Name} property: ");

if (!hasGetAndSet | sameAccess)
{
Console.WriteLine(accessibility);
}
else
{
Console.WriteLine($"\n The get accessor: {getAccessibility}");
Console.WriteLine($" The set accessor: {setAccessibility}");
}
}
}
// The example displays the following output:
// The System.DateTime.Now property:
// PropertyType: DateTime
// Static: True
// Read-only: True
// Indexed: False
//
// Accessibility of the System.Collections.Generic.List`1.Item property: public

참조