반응형

이벤트는 어떠한 문제가 발생했다는 사실을 개체가 시스템의 모든 관련 구성 요소에 전달하는 방법입니다.
보통 클래스내에 발생하는 이벤트를 외부에 전달할 수 있습니다.
+= 연산자를 사용하여 이벤트를 추가할 수 있다.
-= 연산자를 사용하여 이벤트를 제거할 수 있다.

 

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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ConsoleApplication1
{
    class Output
    {
        public event EventHandler print;
        public void totalOutput()
        {
            if (this.print != null)
            {
                print(this, EventArgs.Empty);
            }
        }
    }
 
 
    class Program
    {
        static void Output1(object sender, EventArgs args)
        {
            Console.WriteLine("Output1");
        }
        static void Output2(object sender, EventArgs args)
        {
            Console.WriteLine("Output2");
        }
 
        static void Main(string[] args)
        {
            Output output = new Output();
            output.print += new EventHandler(Output1);
            output.print += new EventHandler(Output2);
            output.totalOutput();
        }
    }
}
 
 

지금은 간단하게 함수를 이벤트에 추가하게되면 이벤트를 호출 시 추가한 함수들이 같이 순차적으로 호출되게 됩니다.

 

그리고 추가와 삭제를 클래스 객체의 get이나 set처럼 add와 remove를 사용하여 추가 삭제가 가능합니다.

 

다음과 같이 사용할 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace ConsoleApplication1
{
    class Output
    {
        private EventHandler _print;
        public event EventHandler print
        {
            add
            {
                _print += value;
            }
            remove
            {
                _print -= value;
            }
        }
        public void totalOutput()
        {
            if (this._print != null)
            {
                _print(this, EventArgs.Empty);
            }
        }
    }
 
 
    class Program
    {
        static void Output1(object sender, EventArgs args)
        {
            Console.WriteLine("Output1");
        }
        static void Output2(object sender, EventArgs args)
        {
            Console.WriteLine("Output2");
        }
 
        static void Main(string[] args)
        {
            Output output = new Output();
            output.print += new EventHandler(Output1);
            output.print += new EventHandler(Output2);
            output.totalOutput();
        }
    }
}
 

이렇게 add와 remove를 사용할 수 있습니다.

private EventHandler _print;

public event EventHandler print

{

       add

       {

                _print += value;

       }

       remove

       {

              _print -= value;

       }

}

 

반응형

'개발공부 > C#' 카테고리의 다른 글

[C#] 이벤트(event) & 델리게이트(delegate)  (0) 2019.10.17
[C#] delegate  (0) 2019.10.16
[C#] yield  (0) 2019.10.14
[C#] 반복문  (0) 2019.10.13
[C#] 조건문  (0) 2019.10.12
반응형

yield는 반복기 함수(iterator method)에서 컬렉션 데이터를 하나씩 리턴하게 할 때 사용하게 됩니다.


yield에는 두 가지 형태의 yield문을 가지고 있습니다.
yield return <수식(expression)>;
yield break;

yield return을 사용하면 호출할 때마다 순차적으로 따로 반환할 수 있습니다.
yield break을 사용하면 바로 반복기를 종료할 수 있게 됩니다.


반복기 함수는 다음과 같은 형식을 가져야합니다.
- 반환 형식은 IEnumerable, IEnumerable, IEnumerator, 또는 IEnumerator여야 합니다.
- 선언에 ref 또는 out 매개 변수가 허용되지 않습니다.


기본 형식

IEnumerable<변수형> elements = MyIteratorMethod();   
foreach (변수형 element in elements)   
{   
    실행문
}   

예제
static void Main(string[] args)
{
foreach (int nNum in funsion())
    Console.WriteLine(nNum);
}

public static IEnumerable funsion()
{
yield return 1;
yield return 2;
yield return 3;
yield return 4;
}
이렇게 구현하면 순차적으로 값을 리턴합니다.
출력 값
1
2
3
4



static void Main(string[] args)
{
foreach (int nNum in multiplication(2))
    Console.WriteLine(nNum);
}

public static IEnumerable multiplication(int nNum)
{
for ( int i = 1; i <= 9; i++ )
yield return nNum * i;
}
반복기 함수에서 for문을 이용해서 리턴할 수 있습니다.
출력 값
2
4
6
8
10
12
14
16
18


static void Main(string[] args)
{
foreach (int nNum in multiplication(2))
Console.WriteLine(nNum);
}

public static IEnumerable multiplication(int nNum)
{
for (int i = 1; i <= 9; i++)
{
if (i == 5) yield break;
yield return nNum * i;
}
}

중간에 yield break를 만나면 종료됩니다.
출력 값
2
4
6
8

반응형

'개발공부 > C#' 카테고리의 다른 글

[C#] delegate  (0) 2019.10.16
[C#] Event  (0) 2019.10.15
[C#] 반복문  (0) 2019.10.13
[C#] 조건문  (0) 2019.10.12
[C#] 연산자  (0) 2019.10.10
반응형

오늘은 반복문에 대해 알아보도록 하겠습니다.

 

반복문은 같은 코드를 계속 반복해야 할 때 사용합니다.

 

예를 들면

int nNum = 0;

Console.WriteLine(nNum++); 

Console.WriteLine(nNum++); 

Console.WriteLine(nNum); 

이런 코드가 있다면

반복문인 for문으로 손쉽게

for(int nNum = 0; nNum <= 2; nNum++ ) 
{ 
    Console.WriteLine(nNum); 
}

만들어서 코드를 알아보기 쉽고 단순하게 만들 수 있습니다.

그리고 만약에 코드가 3개가 아니라 10000개 20000개를 반복할 경우에도 손쉽게 변경할 수 있어서 코드를 유지 보수할 때에도 유용할 수 있습니다.

 

1. do - while문 

일단 블록 안에 실행문을 실행 후 while의 조건식을 확인한 후 조건식 값이 true값을 가지면 다시 블록 안의 실행문을 실행합니다.

루프 실행 후 조건을 확인하기 때문에 한번 이상 실행이 되며, 실행 전에 조건을 확인하는 while과 비교가 됩니다.

중간에 break문을 만나면 어느 문에서 든 블록 문을 빠져나갑니다.

 

do

{

     실행문

} while(조건식);

 

예제

int nNum = 0;
do
{
    Console.WriteLine(nNum);
    nNum++;
} while (nNum < 2);

출력 값

0

1

 

2. while문

while에 지정된 조건식을 확인하여 조건식 값이 true값을 가지면 블록 문의 실행문을 반복해서 실행합니다.

중간에 break문을 만나면 어느 문에서 든 블록 문을 빠져나갑니다.

 

while(조건식)

{

    실행문

}

 

예제

int nNum = 0;
while (nNum < 2)
{
    Console.WriteLine(nNum);
    nNum++;

출력 값

0

1

 

3. for문

for문에 지정된 조건식을 확인하여 조건식 값이 true값을 가지면 블록 문을 반복해서 실행합니다.

중간에 break문을 만나게 되면 블록문을 빠져나갑니다.

 

for( 초기값; 조건식; 증감식)

{

    실행문

}

 

예제

for(int i = 0; i < 2; i++ )
{
    Console.WriteLine(i);
}

출력 값

0

1

 

4. foreach문

배열과 컬렉션을 순회하여 변수에 값을 대입하고, 소스 컬렉션이 비워있으면 종료가 됩니다.

중간에 break문을 만나게 되면 블록 문을 빠져나갑니다.

 

foreach(변수 in 배열, 컬렉션)

{

     실행문

}

 

예제

int[] arrNum = new int[] { 0, 1, 2 };

foreach (int nNum in arrNum)
{
    Console.WriteLine(nNum);
}

 

출력 값

0

1

2

반응형

'개발공부 > C#' 카테고리의 다른 글

[C#] Event  (0) 2019.10.15
[C#] yield  (0) 2019.10.14
[C#] 조건문  (0) 2019.10.12
[C#] 연산자  (0) 2019.10.10
[C#] 데이터 타입  (0) 2019.10.09
반응형

안녕하세요.

 

오늘은 조건문에 대해서 알아보도록 하겠습니다.

 

1. if문

조건문이 true인지 false인지를 확인하여, true이면 다음 구문(블럭문)을 실행합니다.

 

if(조건문)

{

       실행문

}

 

예제

int nNum = 2;
if (nNum == 2)
{
    Console.WriteLine("Num : 2");
}

출력값

Num : 2

 

2. if - else문

조건문이 true인지 false인지를 확인하여, true이면 if 블록 문을 실행하고, 아니면 else 블럭문을 실행합니다.

 

if(조건문)

{

       실행문

}

else

{

       실행문

}

 

예제

int nNum = 5; 
if (nNum == 2) 
{ 
    Console.WriteLine("Num : 2"); 
}

else

{

    Console.WriteLine("Num : " + nNum);    

}

출력 값

Num : 5

 

3. if - else if - else문

if 조건문이 true인지 false인지 확인하여 true이면 if 블록 문을 실행하고, 아니면 else if 조건식을 확인해여 true인지 false인이 확인하여 else if 블럭문을 실행하고, 아니면 else 블럭문을 실행합니다.

 

if(조건문)

{

       실행문

}

else if(조건문)

{

       실행문

}

else

{

       실행문

}

 

예제

int nNum = 5; 
if (nNum == 2) 
{ 
    Console.WriteLine("Num : 2"); 
}

else if (nNum == 5) 
{ 
    Console.WriteLine("Num : 5"); 
}

else

{

    Console.WriteLine("Num : " + nNum);    

}

 

출력 값

Num : 5

 

4. switch-case문

switch문에 조건값에 따라서 case문의 실행문을 실행합니다. 그리고 중간에 break문을 만나게 되면 switch문을 빠져나오게 됩니다. case문에 조건값과 같은 값이 없다면 default문의 실행문을 실행하게 됩니다.  

 

switch(조건값)

{

      case 조건값 1:

             실행문

             break;

      case 조건값 2:

              실행문

             break;

              :

              :

      default:

             실행문

             break;

}

 

예제

int nNum = 2;
switch(nNum)
{
    case 1:
        Console.WriteLine("Num : 1");
        break;
   case 2:
        Console.WriteLine("Num : 2");
         break;
    default:
        Console.WriteLine("default");
        break;
}

출력 값

Num : 2

 

주의점은 break;를 안 넣게 되면 다음 구문이 진행이 됩니다.

예를 들면

int nNum = 2; 
switch(nNum) 
{ 
    case 1: 
        Console.WriteLine("Num : 1"); 
        //break;  제거
   case 2: 
        Console.WriteLine("Num : 2"); 
         break; 
    default: 
        Console.WriteLine("default"); 
        break; 
}

 

출력이 

Num : 1

Num : 2가 출력이 됩니다.

반응형

'개발공부 > C#' 카테고리의 다른 글

[C#] yield  (0) 2019.10.14
[C#] 반복문  (0) 2019.10.13
[C#] 연산자  (0) 2019.10.10
[C#] 데이터 타입  (0) 2019.10.09
[C#] 직렬화(Serialization)  (2) 2019.07.16
반응형

1. 산술 연산자

단항
++ (1씩 증가)
-- (1씩 감소)
+ (양수)
- (음수)

이진 연산자
+(더하기)
-(빼기)
* (곱하기)
/ (나누기)
% (나머지)


2. 부울 논리 연산자

단항
!(논리 부정) - 피연산자의 값이 false일 때 true, 값이 true일 때 false.

이진
&(논리 AND)
|(논리 OR)
^(논리 배타적 OR(XOR))
&&(조건부 논리 AND)
||(조건부 논리 OR)

예제
bool bCheck = false;
Console.WriteLine(!bCheck);
output:True

bool bCheck1 = false;
bool bCheck2 = true;

Console.WriteLine(bCheck1 & bCheck2);
Console.WriteLine(bCheck1 | bCheck2);
Console.WriteLine(bCheck1 ^ bCheck2);
Console.WriteLine(bCheck1 && bCheck2);
Console.WriteLine(bCheck1 || bCheck2);

output:
False
True
True
False
True


3. 비트 및 시프트 연산자

단항
~(비트보수) - 비트를 반대로 하여 피연산자의 비트 보수를 생성

이진
<<(왼쪽 시프트)
>>(오른쪽 시프트)
&(논리 AND)
|(논리 OR)
^(논리 배타적OR(XOR))

예제
uint num1 = 0x2A; // 101010
uint num2 = ~num1;
Console.WriteLine(Convert.ToString(num2, toBase: 2));
output:11111111111111111111111111010101

uint num1 = 0x2A; // 101010
uint num2 = num1 << 2;
Console.WriteLine(Convert.ToString(num2, toBase: 2));
output:10101000

uint num1 = 0x2A; // 101010
uint num2 = num1 >> 2;
Console.WriteLine(Convert.ToString(num2, toBase: 2));
output:1010

uint num1 = 0x2A; // 101010
uint num2 = num1 & 0x1B; // 101010 and 011011
Console.WriteLine(Convert.ToString(num2, toBase: 2));
output:1010

uint num1 = 0x2A; // 101010
uint num2 = num1 | 0x1B; // 101010 or 011011
Console.WriteLine(Convert.ToString(num2, toBase: 2));
output:111011

uint num1 = 0x2A; // 101010
uint num2 = num1 ^ 0x1B; // 101010 xor 011011
Console.WriteLine(Convert.ToString(num2, toBase: 2));
output:110001

4. 같음 연산자
==(같음)
!=(같지 않음)

예제
int num1 = 1;
int num2 = 2;
Console.WriteLine(num1==num2);
output:False

int num1 = 1;
int num2 = 2;
Console.WriteLine(num1!=num2);
output:True

5. 비교 연산자(관계형 연산자)
<(보다 작음)
>(보다 큼)
<=(작거나 같음)
>=(크거나 같음)

예제
int num1 = 1;
int num2 = 2;
Console.WriteLine(num1>num2);
output:False

6. ?: 연산자
구문의 다음과 같습니다
조건 ? A : B
조건이 true이면 A가 false이며 B가 결과값이 됩니다.

예제
int num1 = 1;
int num2 = (num1 == 1) ? 1 : 2;
Console.WriteLine(num2);
output : 1

7. ?? 및 ??= 연산자
??는 null이 아닌 경우 왼쪽 피연산자를 반환하고 그렇지 않다면, 오른쪽 연산자를 평가합니다.
??=는 왼쪽 피연산자가 null일 경우만 오른쪽 피연산자의 값을 왼쪽 피연산자에 대입합니다. 

예제

int? num1 = null;
int num2 = num1 ?? -1;
Console.WriteLine(num2);  
output: -1


List numbers = null;
(numbers ??= new List()).Add(5);
Console.WriteLine(string.Join(" ", numbers));  
output: 5

반응형

'개발공부 > C#' 카테고리의 다른 글

[C#] yield  (0) 2019.10.14
[C#] 반복문  (0) 2019.10.13
[C#] 조건문  (0) 2019.10.12
[C#] 데이터 타입  (0) 2019.10.09
[C#] 직렬화(Serialization)  (2) 2019.07.16
반응형

데이터 타입에 대해 알아보겠습니다.

 

1. 정수 형식

C# 형식 / 키워드 범위 크기 .NET 형식
sbyte -128 ~ 127 부호 있는 8비트 정수 System.SByte
byte 0 ~ 255 부호 없는 8비트 정수 System.Byte
short –32,768 ~ 32,767 부호 있는 16비트 정수 System.Int16
ushort 0 ~ 65,535 부호 없는 16비트 정수 System.UInt16
int –2,147,483,648 ~ 2,147,483,647 부호 있는 32비트 정수 System.Int32
uint 0 ~ 4,294,967,295 부호 없는 32비트 정수 System.UInt32
long –9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 부호 있는 64비트 정수 System.Int64
ulong 0 ~ 18,446,744,073,709,551,615 부호 없는 64비트 정수 System.UInt64

 

다음과 같이 사용할 수 있습니다.

int num = 1234;

 

정수 리터럴은 10진수, 16진수, 2진 리터럴로 다음과 같이 지정할 수 있습니다.

(리터럴 : 변하지 않는 데이터)

var decimalLiteral = 42;

var hexLiteral = 0x2A;

var binaryLiteral = 0b_0010_1010;

 

2. 부동 소수점 숫자 형식

C# 형식 / 키워드 근사 범위 전체 자릿수 .NET 형식
float ±1.5E−45 ~ ±3.4E38 ~6-9개 자릿수 System.Single
double ±5.0E−324 ~ ±1.7E308 ~15-17개 자릿수 System.Double
decimal ±1.0E-28 ~ ±7.9228E28 28-29개의 자릿수 System.Decimal

 

다음과 같이 사용할 수 있습니다.

double dbNum = 12.34;

 

부동 소수점 리터럴은 다음과 같이 지정할 수 있습니다.

d 또는 D 접미사는 리터럴을 double로 변환

f 또는 F 접미사는 리터럴을 float로 변환

m 또는 M 접미사는 리터럴 decimal로 변환

 

double dbNum = 12.34d;

float fNum = 12.34f;

decimal fNum = 12.34m;

 

3. char 형식

형식 범위 크기 .NET형식
char U+0000~U+FFFF 유니코드 16비트 문자 System.Char

 

4. enum 형식

열거형 데이터 타입으로 상수 집합으로 구성이 됩니다.

 

이렇게 정의 할 수 있습니다.

enum Day {Sun, Mon, Tue, Wed, Thu, Fri, Sat};

 

Sun가 0으로 시작되어, Mon에는 1, Tue에는 2와 같은 식으로 데이터가 정의됩니다.

 

그리고 이런 식으로 사용할 수 있습니다.

int x = (int)Day.Sat;

 

int형에 데이터를 넣기 이해서는 명시적 캐스트가 필요합니다.

 

 

5. bool 형식

부울 값인 true / false를 저정할 변수를 선언하는데 사용됩니다.

 

다음과 같이 사용할 수 있습니다.

bool bCheck = true;

 

 

6. Nullable 형식

기본적으로 값형식(int형 등)들은 null값을 가질 수 없습니다. 하지만 null값을 가지기 위해서는 nullable형식으로 만들면 됩니다.

 

값형식에 ?를 붙이거나, Nullable<int>로 null값을 가질 수 있는 데이터를 만들 수 있다. 

 

다음과 같이 사용합니다.

 

int? num1 = null

Nullable<int> num2= null;

 

반응형

'개발공부 > C#' 카테고리의 다른 글

[C#] yield  (0) 2019.10.14
[C#] 반복문  (0) 2019.10.13
[C#] 조건문  (0) 2019.10.12
[C#] 연산자  (0) 2019.10.10
[C#] 직렬화(Serialization)  (2) 2019.07.16
반응형

C# 직렬화

정의 - 직렬화(Serialization)는 개체(Object)를 저장하거나, 메모리(Memory), 데이터베이스(Database) 또는 파일(File)로 전송하기 위해 개체를
바이트 스트림으로 변환하는 프로세스를 말합니다.

목적 - 저장이나 전송 후 다시 개체로 만들기 위함입니다.

역 프로세스 - 역직렬화(deserialization)로 객체를 재구성합니다.

작동방법 - 개체는 스트림(Stream : 데이터, 패킷, 비트 등의 일련의 연속성을 갖는 흐름을 의미)으로 직렬화되어, 데이터뿐 아니라 버전, 문화권 및 어셈블리 이름과 같은 개체 형식에
대한 데이터를 운반할 수 있습니다. 

직렬화 기법

이진 직렬화(Binary Serialization)
이진 직렬화(Binary Serialization)는 이진 인코딩을 사용하여 저장소(Storage:데이터 저장장치) 또는 소켓 기반 네트워크 스트림과 같은
용도에서 사용할 수 있는 압축된 직렬화(Serialization)를 생성합니다.

XML 직렬화(XML Serialization)
XML 직렬화(XML Serialization)는 개체의 public 필드와 속성 또는 메서드의 매개 변수와 반환 값을 특정 XSD(XML 스키마 정의 언어)
문서와 일치하는 XML 스트림으로 직렬화(Serialization) 합니다.


이진 직렬화(Binary Serialization) 실제 구현

전체 소스

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
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO; // 파일 입출력 스트림을 위해 추가 
using System.Runtime.Serialization.Formatters.Binary; // 바이너리 포맷을 위해 추가 
using System.Runtime.Serialization; // IFormatter 인터페이스 사용을 위해 추가 
 
[Serializable]
public class DataObject
{
    public int num1;
    public int num2;
}
 
namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            DataObject data1 = new DataObject();
            data1.num1 = 1;
            data1.num2 = 2;
 
 
            IFormatter formatter = new BinaryFormatter();
 
            Stream streamFileWrite = new FileStream("tttt.txt", FileMode.Create, FileAccess.Write, FileShare.None);
            formatter.Serialize(streamFileWrite, data1);
            streamFileWrite.Close();
 
            Stream streamFileRead = new FileStream("tttt.txt", FileMode.Open, FileAccess.Read, FileShare.None);
            DataObject data2 = (DataObject)formatter.Deserialize(streamFileRead);
            streamFileRead.Close();
 
            Console.WriteLine("num1 : " + data2.num1);
            Console.WriteLine("num2 : " + data2.num2);
 
        }
    }
}
 
cs


세부 설명
1. 이진 직렬화에 필요한 네임스페이스 선언

1
2
3
using System.IO; // 파일 입출력 스트림을 위해 추가 
using System.Runtime.Serialization.Formatters.Binary; // 바이너리 포맷을 위해 추가 
using System.Runtime.Serialization; // IFormatter 인터페이스 사용을 위해 추가 
cs



2. 직렬화할 클래스(혹은 객체)를 정의

1
2
3
4
5
6
[Serializable] 
public class DataObject 
    public int num1; 
    public int num2; 
cs


꼭 [Serializable]를 붙여야 합니다.

3. 구현

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
namespace ConsoleTest 
    class Program 
    { 
        static void Main(string[] args) 
        { 
            DataObject data1 = new DataObject(); 
            data1.num1 = 1
            data1.num2 = 2
 
 
            IFormatter formatter = new BinaryFormatter(); 
 
            Stream streamFileWrite = new FileStream("tttt.txt", FileMode.Create, FileAccess.Write, FileShare.None); 
            formatter.Serialize(streamFileWrite, data1); 
            streamFileWrite.Close(); 
 
            Stream streamFileRead = new FileStream("tttt.txt", FileMode.Open, FileAccess.Read, FileShare.None); 
            DataObject data2 = (DataObject)formatter.Deserialize(streamFileRead); 
            streamFileRead.Close(); 
 
            Console.WriteLine("num1 : " + data2.num1); 
            Console.WriteLine("num2 : " + data2.num2); 
 
            } 
        } 
cs


Stream streamFileWrite = new FileStream("tttt.txt", FileMode.Create, FileAccess.Write, FileShare.None);
formatter.Serialize(streamFileWrite, data1);
streamFileWrite.Close();
여기서 data1 객체를 직렬화한 후 tttt.txt에 저장합니다.
파일로 열어보면 다음과 같습니다.


알아볼 수 없도록 이진화로 되어있는 것을 볼 수 있습니다.

그 후 

Stream streamFileRead = new FileStream("tttt.txt", FileMode.Open, FileAccess.Read, FileShare.None);
DataObject data2 = (DataObject)formatter.Deserialize(streamFileRead);
streamFileRead.Close();

이 부분에서 다시 역직렬화합니다.

출력된 데이터를 확인해보면 제대로 데이터가 역직렬화된 것을 알 수 있습니다.

 

 

 

반응형

'개발공부 > C#' 카테고리의 다른 글

[C#] yield  (0) 2019.10.14
[C#] 반복문  (0) 2019.10.13
[C#] 조건문  (0) 2019.10.12
[C#] 연산자  (0) 2019.10.10
[C#] 데이터 타입  (0) 2019.10.09

+ Recent posts