Windows 환경에서 사용되는 코드입니다.

WIN API 기반으로 이름(2번째 인자)에 해당하는 Named Mutex 를 통해 내부적 처리를 한다고 합니다.

 

using System;
using System.Threading;
using System.Windows.Forms;

namespace WindowsFormsApp1
{
    static class Program
    {
        /// <summary>
        /// 해당 애플리케이션의 주 진입점입니다.
        /// </summary>
        [STAThread]
        static void Main()
        {
            bool isRunning;
            Mutex mutex = new Mutex(true, "UniqueMutexName", out isRunning); //뮤택스 확인

            if (!isRunning) //이미 해당이름으로 Mutex가 걸려있으면 false 없다면 true
            {
                return;
            }
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());


            mutex.ReleaseMutex();  // 해제
        }
    }
}

도구 -> 옵션 -> 환경 -> 클라이언트 성능에 따른 시각적 효과 자동 조정 항목 체크를 해주면 됩니다.

 

클래스, 구조체 등 내부 값이 있는 경우에 IComparer<T> 를 이용하여 비교하여 정렬이 가능합니다.

 

using System;
using System.Collections.Generic;

namespace ConsoleApp
{    
    class Program
    {
        class DescCompare : IComparer<Data>
        {
            //작으면 -1, 같으면 0, 크면 1
            //반대인 경우는 Asc 가능
            public int Compare(Data data1, Data data2)
            {
                return data1.A < data2.A ? -1 : 1;
            }
        }

        class Data
        {
            public int A { get; set; }
            public string B { get; set; }
        }

        static void PrintList (List<Data> datas, string message)
        {
            Console.WriteLine(message);
            foreach (Data data in datas)
            {
                Console.WriteLine(string.Format("{0}) {1}", data.A, data.B));
            }
        }

        public static void Main(string[] args)
        {
            List<Data> datas = new List<Data>();
            datas.Add(new Data { A = 30, B = "Test30" });
            datas.Add(new Data { A = 20, B = "Test20" });
            datas.Add(new Data { A = 10, B = "Test10" });

            PrintList(datas, "정렬 전");
            datas.Sort(new DescCompare());
            PrintList(datas, "정렬 후");
        }
    }
}

 

using System;

namespace Test
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 12345;

            //C, D, N, F, E, X 등 뒤에 숫자는 표현할 자리를 의미합니다.

            //아래식은 모두 ToString() 형태에서 사용 가능합니다!
            //Console.WriteLine("12345 C5 = {0}", a.ToString("C5"));

            Console.WriteLine("12345 C5 = {0:C5}", a); //통화
            Console.WriteLine("12345 D5 = {0:D5}", a); //정수 (0으로 채워집니다.)
            Console.WriteLine("12345 N5 = {0:N5}", a); //3자리마다 콤마
            Console.WriteLine("12345 F5 = {0:F5}", a); //실수형 (자릿수에서 반올림)
            Console.WriteLine("12345 E5 = {0:E5}", a); //지수형
            Console.WriteLine("12345 X5 = {0:X5}", a); //16진수

            Console.WriteLine();
            Console.WriteLine("12345 2진수  = {0}", Convert.ToString(a, 2));
            Console.WriteLine("12345 8진수  = {0}", Convert.ToString(a, 8));
            Console.WriteLine("12345 16진수 = {0}", Convert.ToString(a, 16));

            Console.WriteLine();
            DateTime dateTime = DateTime.Now;
            Console.WriteLine("DateTime.ToString(yyyy-MM-dd hh-mm-ss) = {0}", dateTime.ToString("yyyy-MM-dd hh-mm-ss"));

            Console.WriteLine();
            float b = 1234.5678f;
            Console.WriteLine("1234.5678f F2 = {0:F2}", b);
        }
    }
}

 

결과값

인덱서는 클래스에 [ ] 를 통해 어떠한 값을 설정하거나 가져오는 방법입니다.

 

프로퍼티처럼 get, set 설정을 해줘야 합니다.

 

public 리턴타입 this [타입 변수명]  으로 시작합니다. (int, string 모두 가능합니다!)

 

(복잡해질 수 있어 안쓰겠거니 했는데 많은 변수에 값을 할당할 때

 

일일히 값 체크를 하며 하드코딩 하는 형태가 되는데 사용해보니 조금 더 머리가 덜 아픈 느낌이 듭니다.)

 

 

using System;

namespace ConsoleApp2
{
    class A
    {
        public int a;
        public int b;
        public int c;

        public int this [int index] //인덱서 설정 indexer
        {
            get
            {
                switch (index)
                {
                    case 0:
                        return a;
                    case 1:
                        return b;
                    case 2:
                        return c;
                    default:
                        return 0;
                }
            }
            set
            {
                switch (index)
                {
                    case 0:
                        a = value;
                        break;
                    case 1:
                        b = value;
                        break;
                    case 2:
                        c = value;
                        break;
                    default:
                        break;
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            A a = new A();
            int[] values = { 33, 28, 90 };

            for (int i = 0; i < 3; ++i)
            {
                a[i] = values[i]; //indexer set

                Console.WriteLine(a[i]); //indexer get
            }

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

 

해당 변수를 인덱스화 시켜서 사용하는 방법! 결과입니다.

.ini 파일은 메모장으로도 열리고 initialization 을 의미 합니다.

초기화 할 때 사용할 영역(section), 키(key), 값(value)을 가지고 있습니다.

저처럼 쫄지마세요.. 되게 간단합니다!

 

Write할 때 딱 세 가지만 아시면 됩니다.

1. 섹션이 없다면 섹션이 생성되고 키도 없다면 생성 됩니다. (키, 값이 해당 섹션으로 추가 됩니다.)

2. 동일한 키가 있다면 덮어 씌여집니다.

3. 삭제는 null 값으로 써주면 됩니다. (키 삭제, 섹션 삭제 가능)

 

예제 코드와 결과입니다.

using System;
using System.IO;
using System.Text;
using System.Runtime.InteropServices; //DllImport

namespace ConsoleApp2
{
    class Program
    {
        [DllImport("kernel32.dll")]
        private static extern uint GetPrivateProfileString(string section,
                                                           string key,
                                                           string defaultValue, //키값이 없을 때의 기본 값
                                                           StringBuilder returnedString,
                                                           uint size,
                                                           string filePath);

        [DllImport("kernel32.dll")]
        private static extern bool WritePrivateProfileString(string section,
                                                             string key,
                                                             string value,
                                                             string filePath);

        static void Main(string[] args)
        {
            string filePath = Path.Combine(Environment.CurrentDirectory, "test.ini");

            //파일이 없다면 만들어줍니다. 해당 프로젝트 Debug 폴더에 위치합니다.
            if (File.Exists(filePath) == false)
            {
                File.Create(filePath);
            }

            WritePrivateProfileString("Section1", "key1", "value1", filePath);
            WritePrivateProfileString("Section1", "key2", "value2", filePath);
            WritePrivateProfileString("Section1", "key333", "ooo", filePath);

            System.Threading.Thread.Sleep(1000); //Write 이 후 바로 못 읽기에 시간을 줍니다..

            StringBuilder sb = new StringBuilder { Capacity = 100 }; //프로퍼티 초기화 방식

            GetPrivateProfileString("Section1", "key1", "Nothing..", sb, (uint)sb.Capacity, filePath);
            Console.WriteLine(string.Format("[Section1]'s key1 = {0}", sb.ToString())); //Section1의 key1 값을 가져옵니다.

            GetPrivateProfileString("Section2", "key11", "Nothing..", sb, (uint)sb.Capacity, filePath);
            Console.WriteLine(string.Format("[Section1]'s key11 = {0}", sb.ToString())); //Section2가 없으므로 Nothing..이 나옵니다.

            WritePrivateProfileString("Section1", "key333", null, filePath); //key333을 지웁니다.

            /*
            WritePrivateProfileString("Section1", null, null, filePath); //Section1 전체가 사라집니다.
            */
        }
    }
}

섹션도 더 추가할 수 있어요~!!

 

직렬화의 의미는 데이터를 바이트화 시켜서 저장 및 불러오기가 가능하다고 이해하면 되겠습니다.

기존의 바이너리, 스트림을 이용해서 파일을 접근해도 되지만

시리얼라이저블을 이용하여 아주 편리하게 데이터를 쉽게 불러올 수 있습니다.

그 밖에도 Json, Soap 정도가 있겠지만 우선은 까먹기전에 정리해둡니다!

 

(멤버 변수에 위에 [NonSerialized] 선언을 해주면 해당 변수는 제외할 수 있습니다.)

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary; //BinaryFormatter

namespace ConsoleApp2
{
    class Program
    {
        [Serializable] //클래스는 반드시 시리얼라이즈 가능하다고 명시를 해주어야 가능합니다.
        class TestClass
        {
            public int value1;
            public int value2;
            public string value3;
        }

        private const string fileName = "testFile.txt";

        static void Main(string[] args)
        {
            TestClass testClass = new TestClass();

            testClass.value1 = 30;
            testClass.value2 = 50;
            testClass.value3 = "Test";

            string path = Path.Combine(Environment.CurrentDirectory, fileName);

            //저장 준비
            FileStream fileStream = new FileStream(path, FileMode.Create);
            BinaryFormatter binaryFormatter = new BinaryFormatter();
            
            //저장!
            binaryFormatter.Serialize(fileStream, testClass);
           
            fileStream.Close();
        
            //저장 된 폴더를 여는 작업
            //.NET Framework 에서만 동작됩니다. .NET Core에선 권한이 필요해서 안되네요!
            System.Diagnostics.Process.Start(Environment.CurrentDirectory);


            //다시 읽어오는 코드입니다.
            FileStream fs = new FileStream(path, FileMode.Open);
            BinaryFormatter bf = new BinaryFormatter();

            TestClass t = (TestClass)bf.Deserialize(fs); //object 타입으로 반환되므로 캐스팅 해줍니다.

            Console.WriteLine("읽어 온 값 : ");
            Console.WriteLine("value1 : {0}, value2 : {1}, value3 : {2}", t.value1, t.value2, t.value3);

            fs.Close();
        }
    }
}

 

파일을 열어볼 수 있게 txt파일로 저장했습니다.

 

실행 결과

 

1. int, float 등은 숫자 입력이지만

    ? (물음표) 키워드를 이용해서 명시적으로 null 값을 줄 수 있습니다.

 

2. ?연산자로 null값이면 실행되지 않도록 조건문 대신 사용할 수 있습니다.

 

3. ?? 두개를 통해 null 값이면 다른 값으로 설정합니다.

 

코드가 짧아지는 장점이 있습니다.

하지만 무분별하게 사용하게 되면 가독성을 헤치게 됩니다.

using System;
using System.Collections;

namespace ConsoleApp
{
    class Program
    {
        public static void Main(string[] args)
        {
            int? a = null;
            Console.WriteLine("a의 값 {0}", a);
            float? b = null;
            Console.WriteLine("b의 값 {0}", b);


            ArrayList arraylist = null;
            arraylist?.Add(30); //arraylist가 null이 아니면 Add(30)
            /*
            if (arraylist == null)
            {
                arraylist.Add(30);
            }
            */

            Console.WriteLine(arraylist?[0]); //null 이지만 오류가 나지 않습니다.

            a = a ?? 20; //a가 null이면 20
            b = b ?? 30.9f; //b가 null 이면 30.9f
            Console.WriteLine("a = {0}, b = {1}", a, b);
        }
    }
}

 

[실행 결과] null은 공백으로 출력됩니다.

 

1. 바이너리 파일로 저장하고 읽어오는 코드입니다.

 여러 타입을 저장할 수 있는데 읽어올 때 반드시 저장된 순서대로 읽어줘야 됩니다.

 (문자열은 앞에 문자열의 길이 + 문자열이 저장되어 해당 길이만큼 읽어옵니다.)

using System;
using System.IO; //...Stream

namespace ConsoleApp
{
    class Program
    {
        public static void Main(string[] args)
        {
            //바이너리 파일 생성 및 저장
            FileStream fs = new FileStream("Test.bin", FileMode.Create);
            BinaryWriter bw = new BinaryWriter(fs);

            string input1 = Console.ReadLine();
            int input2 = Convert.ToInt32(Console.ReadLine());
            float input3 = Convert.ToSingle(Console.ReadLine());

            bw.Write(input1); //string
            bw.Write(input2); //int
            bw.Write(input3); //float

            bw.Close();
            fs.Close();

            //바이너리 파일 읽어오기
            FileStream fs2 = new FileStream("Test.bin", FileMode.Open);
            BinaryReader br = new BinaryReader(fs2);

            Console.WriteLine(br.ReadString()); //string
            Console.WriteLine(br.ReadInt32()); //int
            Console.WriteLine(br.ReadSingle()); //float

            br.Close();
            fs2.Close();
        }
    }
}

 

입력을 받고 읽어온 모습
저장된 데이터

 

2. StreamReader/StreamWriter 를 이용한 모습입니다. 문자열 그대로 저장이 됩니다.

using System;
using System.IO; //...Stream

namespace ConsoleApp
{
    class Program
    {
        public static void Main(string[] args)
        {
            StreamWriter sw = new StreamWriter("Test.dat");
            
            for (int i = 0; i < 5; ++i) //5개의 입력
            {
                sw.WriteLine(Console.ReadLine());
            }

            sw.Close();

            StreamReader sr = new StreamReader("Test.dat");

            while (!sr.EndOfStream) //sr.ReadLine() == null인 경우입니다.
            {
                Console.WriteLine(sr.ReadLine());
            }

            sr.Close();
        }
    }
}

5개의 입력을 받고 다시 출력해준 모습
저장된 데이터

 

보안적인 측면은 바이너리 코드가 읽기가 어려워지기 때문에 더 좋습니다.

아니면 암호화나 해시를 하는 방법을!

+ Recent posts