2019년 5월 23일 목요일

파일 존재 확인(c#)

 string _Filestr = _path;
        System.IO.FileInfo fi = new System.IO.FileInfo(_Filestr);

        if (fi.Exists)
        {
            //파일 존재
            __testGetData = FlexDataControl.GetStructFromFile(_path);
        }
        else
        {
            //파일 없음
        }

2019년 5월 20일 월요일

전략 패턴(많은 if문 피하기)

거리 환산 프로그램을 다시 생각해본다.

예를 들어, 야드에서 피트로 변환하는 방법을 생각해보겠습니다.
야드를 그냥 피트로 변환할 수도있지만 일단 야드에서 미터로 변환하고 나서 미터에서 피트로 변환해도 동일한 결과가 나옵니다.
인치에서 피트로 변환할 경우에도 인치에서 미터로 변환하고 그것을 피트로 변환해도 결과를 구할 수 있습니다.
다시 말해 어떤 변환이라도 일단 미터로 변환한 후에 처음에 목표로 정한 단위로 변환할 수 있습니다.




Convert에 공통되는 메서드와 속성을 정의한다.

public abstract class ConvertBase {
    // 미터와의 비율(이 비율을 곱하면 미터로 변환된다)
    protected abstract double Ratio { get; set; }

    // 거리의 단위 이름(예를 들어 미터 , 피트 등)
    public abstract string UnitName { get; }

    // 미터로부터 변환
    public double FromMeter(double meter)
    {
        return meter / Ratio;
    }

    // 미터로 변환
    public double ToMeter(double feet)
    {
        return feet * Ratio;
    }
}

FromMeter, ToMeter라는 두 개의 메서드는 모든 서브 클래스에서 공통으로 이용하는 메서드입니다.
이러한 구체적인 처리 코드가 있기 때문에 IConverter라는 인터페이스가 아닌 클래스로 지정한것입니다.

그리고 ConverterBase 클래스의 인스턴스를 직접 생성하지는 않을 것이므로 abstract를 붙여 추상클래스로 지정했습니다. ConverterBase 클래스에서 Ratio라는 속성이 미터와의 비율을 나타내는데 이 클래스에 구체적인 값을 넣어둘 수 없으므로 abstract로 지정해서
서브 클래스에서 반드시 구현하게 합니다.
UnitName 속성도 마찬가지로 서브 클래스에서 내용을 정의할 것이므로 abstract로 지정했습니다.

FromMeter 메서드와 ToMeter 메서드는 Ratio의 값이 정해지면 어떤 서브 클래스에서도
동일한 로직을 통해 계산할 수 있으므로 상속하는 부모인 ConverterBase 클래스에서
내용을 정의했습니다.

Converter의 구상 클래스 정의



public class MeterConverter : ConvertBase{
    protected override double Ratio
    {
        get { return 1; }
    }
    public override string UnitName
    {
        get { return "미터"; }
    }
}
public class FeetConverter : ConvertBase {
    protected override double Ratio
    {
        get { return 0.3048; }
    }
    public override string UnitName
    {
        get { return "피트"; }
    }
}
public class InchConverter : ConvertBase {
    protected override double Ratio
    {
        get{ return 0.0254; }
    }
    public override string UnitName
    {
        get{ return "인치"; }
    }
}
public class YardConverter : ConvertBase {
    protected override double Ratio
    {
        get{ return 0.9144; }
    }
    public override string UnitName
    {
        get{ return "야드"; }
    }
}






네 개의 서브 클래스에서는 상속해준 부모 클래스에 있는 abstract 멤버를 오버라이드했습니다.
각 클래스의 차이를 일목요연하게 볼 수 있습니다. MeterConverter 클래스도 정의돼 있습니다. 이 클래스를 정의해두면 '피트에서 미터로 그리고 미터에서 인치로' 같은 방식으로 변환할 수 있습니다.

거리를 단위 변환하는 클래스를 정의한다.

각 Converter 클래스를 정의했으므로 이번에는 이 응용 프로그램이 본래 가져야 할 기능인'거리를 단위 변환하는' 클래스를 정의하겠습니다. 이 클래스의 이름은 DistanceConverter라고 지정합니다.

public class DistanceConverter {
    public ConvertBase From { get; set; }
    public ConvertBase To { get; set; }

    public DistanceConverter(ConvertBase from, ConvertBase to)
    {

        From = from;
        To = to;
    }

    public double Convert(double value)
    {
        var meter = From.ToMeter(value);
        return To.FromMeter(meter);
    }
}








텍스트 파일을 처리하는 프레임워크

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
namespace TextFilePorcessor
 
{
 
    public abstract class TextProcessor
 
    {
 
 
 
        public static void Run<T>(string fileName) where T : TextProcessor, new()
 
        {
 
            var self = new T();
 
            self.Process(fileName);
 
        }
 
 
 
        private void Process(string fileName)
 
        {
 
            Initialize(fileName);
 
            using (var sr = new StreamReader(fileName))
 
            {
 
                while (!sr.EndOfStream)
 
                {
 
                    string line = sr.ReadLine();
 
                    Execute(line);
 
                }
 
            }
 
            Terminate();
 
        }
 
 
 
        protected virtual void Initialize(string fname) { }
 
        protected virtual void Execute(string line) { }
 
        protected virtual void Terminate() { }
 
    }
 
}
cs

TextProcessor 클래스는 abstract(추상)를 붙였으므로 TextProcessor 클래스로부터 상속되다는 것을 전제로 한 클래스입니다.

Run이라는 정적 메서드는 형의 인수를 전달받는 제네릭 메서드입니다.
where T: TextProcessor는 형 T가TextProcessor이거나 TextProcesor를 상속한 서브 클래스
그 뒤에 있는 new()는 형 T가 인수를 가지지 않는 생성자로 인스터스를 생성할 수 있다는 것을 나타냅니다. 이  Run 메서드 안에서는 형 인수 T의 인스턴스를 생성하고 Process 메서드를 호출합니다.

Process 메서드에서는 Initialize, Execute, Terminate 라는 세 개의 메서드를 호출합니다.
이 부분이 다형성을 활용한 부분입니다.
소스코드 상에서는 자기 자신(TextProcessor 클래스)에게 있는 메서드를 호출하지만 실제로 동작할 때는 생성된 인스턴스에 있는 메서드가 호출됩니다.
다형성의 본질인 '다른형을 동일시한다' 라는 개념이 여기에 적용된 것입니다.

Initialize, Execute, Terminate라는 세 개의 메서드는 protected virtual로 지정돼 있으므로
서브클래스에서 오버라이드해서 구체적인 처리를 구현합니다. 아무런 작업도 하지 않아도 된다면 오버라이드하지 않아도 됩니다.




TextProcessor을 이용한 응용 프로그램

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using TextFilePorcessor;



namespace LineCounter
{
    class Program:MonoBehaviour {


        private void Start()
        {
            TextProcessor.Run<LineCounterProcessor>("fff");
        }
    }
}

public class LineCounterProcessor:TextProcessor  {
    private int _count;

    protected override void Initialize(string fname)
    {
        //base.Initialize(fname);

        _count = 0;
    }
    protected override void Execute(string line)
    {
        //base.Execute(line);

        _count++;
    }
    protected override void Terminate()
    {
        //base.Terminate();
        Debug.Log(_count + "줄");
    }


}

이 콘솔 응용 프로그램 프로젝트의 소스에서는 LineCounterProcessor 클래스에 Initialize, Execute, Terminate라는 세 개의 메서드를 정의했는데 이 메서드를 호출하는 코드는 존재하지 않습니다.
이 메서드를 호출하는 것은 클래스 라이브러리 쪽에 있는 TextFileProcessor 클래스입니다.

여기서는 부모 클래스(TextProcessor)에서 코드의 틀(템플릿)을 마련하고 서브 클래스(LineCounterProcessor)에서 부모 클래스에 있는 메서드를 오버라이드해서 서브 클래스 고유의 기능을 구현했습니다. 이런 패턴을 템플릿 메서드 패턴이라고 합니다. 템플릿 메서드 패턴을 이용하면 서브클래스를 정의하기만 하면 되고 전체적인 처리의 흐름을 작성할 필요가 없기 때문에 '닯았지만 조금 다른 코드'가 양산되지 않게 됩니다.

















다형성 기초 abstract class(추상클래스)

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
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GreetingTest : MonoBehaviour {
    // Use this for initialization
    void Start()
    {
        var greetings = new List<GreetingBase>();
        greetings = new List<GreetingBase> { new GreetingMorning(), new GreetingAfternoon(), new GreetingEvening() };
        //greetings.Add(new GreetingMorning());
        greetings.Add(new GreetingBase()); //추상클래스 또는 인터페이스의 인스턴스를 만들수 없음
        foreach (var obj in greetings)
        {
            Debug.Log(obj.GetMessage());
        }
        //결과 GoodMorning 2
        //Good Afternoon 1
        //Good night 1
        //추상클ㄹ
    }
    // Update is called once per frame
    void Update () {
}
}
abstract class GreetingBase
{
    public virtual string GetMessage()
    {
        return "";
    }
}
class GreetingMorning : GreetingBase
{
    public override string GetMessage()
    {
        return "GoodMorning";
    }
}
class GreetingAfternoon : GreetingBase
{
    public override string GetMessage()
    {
        return "Good Afternoon";
    }
}
class GreetingEvening : GreetingBase
{
    public override string GetMessage()
    {
        return "Good night";
    }
}
cs

2019년 5월 17일 금요일

Linq 예제 (2)

public class Category
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public override string ToString()
        {
            return "ID :"+ ID +",분야 이름 :" + Name;
        }
    }

    public class Book {
        public string Title { get; set; }
        public int Price { get; set; }
        public int CategoryId { get; set; }
        public int PublishedYear { get; set; }
        public override string ToString()
        {
            return "발행연도 :"+ PublishedYear+", 분야 :"+CategoryId + ", 가격 :"+Price+",제목 :"+Title;
        }
    }

    public static class Library {
        // Categories 속성을 통해 나온 분야의 목록을 구할 수 있다.
        public static IEnumerable<Category> Categories { get; private set; }

        //Books 속성을 통해 위에 나온 서적 정보를 구할 수 있다.
        public static IEnumerable<Book> Books { get; private set; }

        static Library(){
            //Categories와 Books에 데이터를 설정한다. 자세한 구현 내용은 생략
        }
    }





특정 분야 내에서 가장 비싼 가격을 구하는 코드
 
1
2
3
 var price = Library.Books
            .Where(b => b.CategoryId == 1)
            .Max(b => b.Price);
cs



최소값인 요소를 한 개만 구하기

1
2
3
4
5
   var min = Library.Books
                         .Min(x => x.Title.Length);
   var book = Library.Books
                         .First(x => x.Title.Length == min);
cs


평균값 이상인 요소를 모두 구한다.

1
2
3
4
5
6
7
8
9
10
11
12
        var average = Library.Books
                            .Average(x => x.Price);
        var aboves = Library.Books
                            .Where(b => b.Price > average);
        foreach(var x in aboves)
        {
            Debug.Log("x:" + x);
        }
cs


중복제거

1
2
3
4
5
6
7
8
9
10
11
12
13
처음 Select 메서드로 발행연도만 구하고 distinct 메서드로 중복을 제거하고 마지막에 OrderBy 메서드로 정렬합니다.
        var query = Library.Books
                            .Select(b => b.PublishedYear)
                            .Distinct()
                            .OrderBy(y => y);
        foreach (var n in query)
        {
            Debug.Log("발행연도 :" + n);
        }
cs


여러 개의 키로 나열한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
여러 개의 키로 나열하려면 OrderBy 또는 OrderByDescending 메서드 뒤에 ThenBy 메서드 나 ThenByDescening 메서드를 호출합니다.
        var books = Library.Books
                            .OrderBy(b => b.CategoryId)
                            .ThenByDescending(b => b.PublishedYear);
        foreach(var x in books)
        {
            Debug.Log("xxxx :" + x);
        }
위의 코드에서는 CategoryId, PublishedYear라는 순서로 나열했습니다. PublishedYear는 ThenByDescending 메서드로 정렬했으므로 최신 발행연도부터 표시됩니다.
cs


여러 요소 가운데 어느 하나에 해당하는 객체를 구한다.


1
2
3
        var years = new int[] { 20132016 };
        var books = Library.Books
                            .Where(b => years.Contains(b.PublishedYear));
cs



GroupBy 메서드로 그룹화한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
GroupBy 메서드를 사용하면 지정한 키마다 요소를 그룹화할 수 있습니다.
발행연도를 기준으로 서적을 그룹화하는 예를 살펴보겠습니다.
        //발행연도를 기준으로 그룹화한다.
        var groups = Library.Books
                            .GroupBy(b => b.PublishedYear)
                            .OrderBy(g => g.Key);
       
        // key가 PublishedYear인 Dictionary생성됨
        //IEnumerable<IGrouping<int,Book>>
        foreach (var g in groups)
        {
            Debug.Log("년:" + g.Key);
            foreach (var book in g)
            {
                Debug.Log(" " + book);
            }
        }
cs


각 그룹에서 최댓값을 가진 객체를 구한다.


1
2
3
4
5
6
7
8
9
10
  //각 발행연도 그룹에서 가장 가격이 비싼 서적을 구한다.
        var selected = Library.Books
                                .GroupBy(x => x.PublishedYear)
                                .Select(b => b.OrderByDescending(a => a.Price).First())
                                .OrderBy(o => o.PublishedYear);
        foreach (var book in selected)
        {
            Debug.Log(book.PublishedYear + "년 제목:" + book.Title + "가격 :" + book.Price);
        }
cs



ToLookup으로 발행연도별로 그룹화한다.
1
2
3
4
5
6
7
8
9
10
11
        var lookup = Library.Books
                            .ToLookup(x => x.PublishedYear);
        var books = lookup[2014];//키를 PublishedYear로 가지는
        foreach (var b in books)
        {
            Debug.Log("book :" + b);
        }
cs


join메서드


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
        //Books 와 Categories를 분야 ID로 결합한다
        var books = Library.Books
                           .OrderBy(b => b.CategoryId)
                           .ThenBy(b => b.PublishedYear)
                           .Join(Library.Categories,
                                book => book.CategoryId,
                                category => category.ID,
                                (book, category) => new
                                {
                                    Title = book.Title,
                                    Category = category.Name,
                                    PublishedYear = book.PublishedYear,
                                }
                           );
        foreach (var book in books)
        {
            Debug.Log(book.PublishedYear + "년 제목:" + book.Title + "카테고리 :" + book.Category);
        }
        //join 메서드가 받는 네 개의 인수는 다음과 같다.
        //첫 번째 인수: 결합할 두 번째 시퀀스
        //두 번째 인수: 대상 시퀀스의 결합에 사용할 키
        //세 번째 인수: 두 번째 시퀀스의 결합에 사용할 키
        //네 번째 인수: 결합한 결과로 얻어지는 객체를 생성하는 함수
cs


2016년에 발행된 분야의 목록을 구한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
        var names = Library.Books
                            .Where(b => b.PublishedYear == 2016)
                            .Join(Library.Categories,
                                book => book.CategoryId,
                                category => category.ID,
                                (book, category) => category.Name)
                            .Distinct();
        foreach (var name in names)
        {
            Debug.Log(name);
        }
        //일단 where 메서드로 2016년에 발행된 서적만 골라냅니다. 이렇게 골라낸 서적의 목록과 분야의 목록을 join 메서드로 결합합니다.
        //구하는것은 이름 뿐이므로 마지막 인수에는 category의 name속성만 반환하는 람다식을 넘겨줍니다.
        //마지막에는 Distinct로 중복제거
cs


 GroupJoin으로 그룹화해서 결합한다.


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
        var groups = Library.Categories
                            .GroupJoin(Library.Books,
                            c => c.ID,
                            b => b.CategoryId,
                            (c, b) => new
                            {
                                Category = c.Name,
                                Books = b,
                            });
        foreach (var group in groups)
        {
            Debug.Log(group.Category);
            foreach (var book in group.Books)
            {
                Debug.Log("title :" + book.Title + "발행년도 :" + book.PublishedYear);
            }
        }
//join 메서드와 차이점은 결과가 2차원의 표 형식이 되는 것이 아니고 분야 아래에
여러 서적이 늘어서는 계층 형식으로 결합된다 라는점입니다.
cs




2019년 5월 16일 목요일

수준 높은 쿼리 예제

책에 그렇게 적혀있지 사실 수준이 높지는 않음
(질의쿼리가 훨씬 수준 높다 ㅋ)

집필한 서적이 두 권 이상인 저자를 구한다


1
var authors = db.Autors.Where(a => a.Books.Count() >= 2);
cs

서적을 출판연도, 저자 이름 순서(각각 오름차순)로 정렬해서 구한다.

1
2
3
var books = db.Books
                 .OrderBy(b=>b.PublishedYear)
                 .ThenBy(b=>b.Author.Name);
cs

각 발행연도에 해당하는 서적 수를 구한다.


1
2
3
4
5
6
7
8
9
10
11
var groups = db.Books
                  .GroupBy(b=>b.Publishedyear)
                  .Select(g=>new{
                    Year = g.key,
                    Count = g.Count()
                    });
foreach(var g in groups)
{
Debug.Log(g.Year,g.Count);
}
cs

집필한 서적이 가장 많은 저자 한 명을 구한다.


1
2
3
4
5
6
7
var author = db.Authors
                 .Where(a=>a.Books.Count() ==
                            db.Authors.Max(x=>x.Book.Count()))
                 .First();
Debug.Log( author.name,author.Gender,author.Birthday);
cs





git rejected error(feat. cherry-pick)

 문제 아무 생각 없이 pull을 받지않고 로컬에서 작업! 커밋, 푸시 진행을 해버렷다. push에선 remote와 다르니 당연히 pull을 진행해라고 하지만 로컬에서 작업한 내용을 백업하지 않고 진행하기에는 부담스럽다(로컬작업 유실 가능성) 해결하려...