dngchn's [C#]2017. 7. 20. 16:25

Code Snippet 만들어 사용하기


Code snippet이란 '코드 조각'으로, 반복되는 패턴의 코드 덩어리를 빠르게 입력할 수 있는 일종의 매크로 기능이다.


예를 들어, C#기준으로 설명하자면(VB나 Visual Studio에서 지원되는 다른 언어도 마찬가지, xml까지)

코드 중간에 prop 라고 치고 탭을 따닥 누르면(아주 빨리 누르지 않아도 됨),


public int MyProperty { get; set; }


위와 같이 자동 입력된다.

또한 코드조각이 입력된 후 상태가 int가 선택된 상태에서 커서는 int 앞에 위치해있게된다. 바로 원하는 대로 타입을 입력하고, 탭을 두번 누르면 다음으로 MyProperty에 위치한다. 마찬가지로 원하는 이름으로 변경 후, 엔터를 누르면,

삽입된 코드의 끝으로 나가게 된다.


prop이외에도 이미 많은 조각들이 내장되어 있다. 억지로 쓸 필요는 없지만, 이런게 있는 줄 모르고 안쓰는 것 보단,

어떤것들이 있는지 살펴본 후 필요할 때 적절히 활용하면 코딩 생산성을 높일 수 있고, 궁극적으로는 퇴근 시간을 앞당기어 개발자들이 원하는 저녁있는 삶을 추구하는데 도움을 줄 수도 있으리라...


사용은 이렇게 하면 된다. 특별히 내가 원하는 조각이 있다면, 직접 만들어 사용할 수 도 있다. 조금 까다롭게도 여겨지지만, 처음 부터 새롭게 만들기 보단, 이미 내장된 것 들을 잘 활용하면, 내가 원하는 것들을 손쉽게 만들어 쓸 수 있다.


검색을 해보면, Code Snippet Editor(http://snippeteditor.codeplex.com/)등과 같이 코드 조각을 만드는데 도움을 주는 도구들도 있겠지만, 편함을 위해 무언가 새로운 것의 도움을 받기 보단, 아래와 같은 방법으로 한번 따라해보면 손쉽게 만들어 쓸 수 있으니 개인적으로는 Editor까지 쓸필요는 없을 것 같다.


아래 예 대로만 따라하자.


1. Visual Studio에서 새로만들기 추가에서 xml을 선택한다.




2. 그럼 아래와 같이 기본 xml파일이 만들어 진다. 2번째 줄로 커서를 위치 시키고, Ctrl + K + S 를 누른다.




3. 그럼 아래와 같은 화면을 볼 수 있고, Snippet를 눌러 선택한다.



4. 그럼 아래와 같은 상태가 된다.



5. 위에서 title부터 description까지는 기본 정보이다. 적절하게 채워주자. 단 여기서 Shortcut에 해당하는 것이 위의 예에서 prop이다. 즉 코드 조각을 불러내는 단축단어이니 나중에 쉽게 생각나도록 짧고 강렬하게(?) 잘 짓자.


6. 아래쪽 Literal과 Code를 채워주는 것이 핵심이다. 이 부분은 기존 것을 불러내어 보면, 어떻게 입력해야 할지 감이 올 것이다. 이를 위해 Tool - Code Snippets Manager를 선택하여 창을 띄운다(영문메뉴 기준).




7. Code Snippets Manager에서 Language를 CSharp으로 하고 항목 중 Visual C#으로 선택하면 위와 같이 이미 내장된 많은 snippet들을 볼 수 있다. 위 snippet 목록은 실제 위의 파일 경로에 각각의 파일로 존재한다. 이해를 위해 propfull을 열어본다.




8. 위에서 Literal 부분을 보자. 정의된 3개의 Literal들인 type, property, field가 결국 아래 Code 부분에 미리 작성된 조각코드에 쓰이게 된다. 코드에서는 각각의 Literal들이 $가 앞뒤로 붙어 $type$, $property$, $field$와 같이 되어있다. 물론 이 이름들은 원하는대로 만들면 된다($end$는 코드의 끝을 의미한다. 그대로 두자).


9. 위의 코드 부분은 가능하면 복사하여 내가 만드는 코드 조각에 붙여넣자. 그래야 코드조각이 코드에 삽입되었을 때 줄바꿈과 들여쓰기 등을 깔끔하게 가져갈 수 있다(복사 후 Literal등 변경 부분만 수정하길 추천).


10. 마지막 Language부분이 csharp로 되어있음에 주의. 다른 언어라면 해당 언어대로 변경해줘야 한다.


11. 거의 다 되었다. 위에서 만든 .xml파일을 .snippet으로 확장자를 바꿔준다(내장 snippet과 마찬가지로 파일로 복사되어 관리되므로, 파일 이름도 적절히 짓자).


12. 다시 Code Snippets Manager로 와서 위에서 저장한 파일을 선택하여 import시켜준다.

import된 snippet는 Manager에서 My Code Snippets 폴더에 복사되어 관리된다.


13. 잘 되었는지 확인은, Code Snippets Manager에서 My Code Snippets 폴더에 잘 들어갔는지 확인하고,

최종적으로 코드창에서 내가 입력한 Shortcut을 적고 따닥~ 해보면 된다.

Posted by dngchn
dngchn's [C#]2017. 6. 22. 11:02

위와 같이 Visual Studio 2017에서 무언가 실행했는데, 지금까지 못보던 창이 따라 뜬다.

일단 디버그 모드로 실행했을 경우만 뜨니, 디버깅을 위한 무언가라고 짐작이 된다.

눈에 거슬리면 아래와 같은 옵션에서 체크 해제하면 안뜬다.




Posted by dngchn
dngchn's [C#]2017. 6. 8. 10:30

예제를 통해 MEF 겉을 사알짝 핥아 보자.


1. 먼저 컴포넌트를 정의 해보자.

namespace Contract
{
    public interface IComponent
    {
        string Description { get; }
        string ManipulateOperation(params double[] args);
    }

    public interface IMetadata
    {
        char Symbol { get; }
    }
}

위에서 정의한 컴포넌트는 Description이란 이름으로 컴포넌트에 대한 설명 string과,

ManipulateOperation이라는 메서드(무언가 동작하는)를 갖는다.

추가적으로 IMetadata 인터페이스를 정의하여 Symbol이라는 이름의 프러퍼티를 정의하자.


2. 컴포넌트의 정의대로 특정 기능을 맞추어 외부로 export 하는 놈을 만들자.

using System.ComponentModel.Composition;
using Contract;

namespace ExportMultiplyLib
{
    [Export(typeof(IComponent))]
    [ExportMetadata("Symbol", '*')]
    public class MultiplyOfNumberComponent : IComponent
    {
        public string Description
        {
            get { return "Multiplication of components"; }
        }

        public string ManipulateOperation(params double[] args)
        {
            string result = "";
            double count = 1;

            foreach (double d in args)
            {
                count *= d;

                result += d.ToString() + " * ";
            }

            return result.Trim('*', ' ') + " = " + count.ToString();
        }
    }
}

위에서 보는대로 위 클래스는 IComponent를 구현한다. 클래스의 첫단에 attribute를 두어

IComponent타입으로 export함을 표시하고, 추가적으로 Metadata도 export한다.

이런 attribute 정보는 외부에서 검색(?)할때 판별할 정보가 된다. 즉 이러한 정보를 기준으로

원하는 컴포넌트를 찾게 되는 것이다.


3. 이제 이렇게 export된 놈을 갖다 쓰는 놈을 만들자.

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.IO;
using System.Reflection;
using Contract;

namespace ImportingLib
{
    public class Importer
    {
        [ImportMany(typeof(IComponent))]
        private IEnumerable<Lazy<IComponent, IMetadata>> operations;

        public void DoImport()
        {
            var catalog = new AggregateCatalog();

            catalog.Catalogs.Add(new DirectoryCatalog(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)));

            CompositionContainer container = new CompositionContainer(catalog);

            container.ComposeParts(this);
        }

        public int AvailableNumberOfOperation
        {
            get { return operations != null ? operations.Count() : 0; }
        }

        public List<string> CallAllComponent(params double[] args)
        {
            var result = new List<string>();

            foreach (Lazy<IComponent, IMetadata> com in operations)
            {
                Console.WriteLine(com.Value.Description);
                Console.WriteLine(com.Metadata.Symbol);
                result.Add(com.Value.ManipulateOperation(args));
            }

            return result;
        }
    }
}

위의 Importer는 클래스 정의 위에는 특별한 attribute는 없다.

멤버중 "operations"는 IComponent 타입의 컴포넌트를 여러개(ImportMany) 갖는 열거형 멤버이다.

Lazy<IComponent, IMetadata>를 통해 IComponent와 IMetadata타입을 참조한다.


DoImport 메서드에서는 카탈로그를 정의하고 카탈로그에 컴포넌트가 있는 위치를 지정한다.

즉 해당 위치에서 목록(카탈로그)을 구성하라는 의미이다.


CompositionContainer타입의 컨테이너를 생성하며 위에서 준비해 놓은 카탈로그를 기반으로 한다.

최종적으로 컨테이너의 ComposeParts를 호출하여 dll들을 검색하여 컴포넌트를 컨테이너에 담아둔다.


프러퍼티로서 AvailableNumberOfOperation은 컴포넌트의 수를 나타낸다.


메서드 CallAllComponent는 모든 컴포넌트의 동작을 실행시킨다.

동작에는 우선 컴포넌트의 Description을 표시하고,

메타데이터의 심볼을 표시한다.

그리고 각 컴포넌트 고유의 기능을 수행한 결과를 표시한다.


4. 위의 Importer를 실행시켜 보자. 간단한 콘솔프로그램으로 한다.

using System;

namespace MEFTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var t = new ImportingLib.Importer();

            t.DoImport();

            Console.WriteLine("{0} component(s) are imported successfully.", t.AvailableNumberOfOperation);
            var result = t.CallAllComponent(125, 5, 10, 27, 45, 19, 10);
            foreach (string s in result)
            {
                Console.WriteLine(s);
            }
            Console.Read();
        }
    }
}

Importer객체를 하나 생성해서 DoImport를 실행시킨다.

그리고 CallAllComponent를 실행시킨다. 인자로서 숫자들을 넘긴다.

이는 CallAllComponent 메서드의 인자인 double[]로 넘겨진다.

CallAllComponent는 string타입 List를 반환하므로, 각 string을 표시한다.


5. 예시 결과는 아래와 같다.


위에서는 곱셈 컴포넌트 하나만 코드로 살펴봤다. 동일한 방식으로 뺄셈, 덧셈도 만들어 실행한 결과이다.


* 실행시 주의할점.

각 exporter들의 output path를 실행파일의 경로로 일치시켜 주던가, 아니면 컴파일 후 dll들을 실행파일 경로에 복사해 넣어주어야 한다.

각 컴포넌트를 구성하는 dll들은 동적으로 검색되어 실행되므로, 몇가지 dll들을 빼고 실행시키면 존재하는 dll들에 대해서만 실행된다.


* 프로젝터 소스를 첨부한다.

MEFTest.zip


* 코드올리기

찾아보니 티스토리에 코드를 이쁘게 올리는 방법을 소개한 자료가 많더라(SyntaxHighlighter ?).

난 그렇게 하기도 귀찮아서, 아래 온라인 사이트에 코드 붙여넣어 스타일 얻어 다시 복붙했다.

(http://www.pvladov.com/p/syntax-highlighter.html)































Posted by dngchn
dngchn's [C#]2017. 6. 7. 18:16

클래스나 멤버 선언시 Scope(접근 한정자)를 별도로 지정하지 않으면 default로 주어지는 scope는 무엇이 될까?


클래스는 internal

멤버는 private


확인방법은 구글 검색이 아닌,

Visual Studio에서 'Object browser'를 통해 해당 클래스나 멤버를 선택하여 나타나는 정보를 보면 된다.

Posted by dngchn
dngchn's [C#]2016. 7. 27. 20:38

https://www.youtube.com/watch?v=TpKcAmaaBts

Posted by dngchn
dngchn's [C#]2014. 11. 6. 11:46

Random Number

亂數

亂(어지러울 란)자를 쓰니, 어지러운 수, 규칙없는 수가 되겠다.

굳이 우리말로 하자면.. 음.. 뭘까.. 꼴림수...?

암튼 난수가 필요하다.

아이스크림 쏘기 사다리를 타거나,

나 같은 경우는 디비에 더미데이터를 왕창 때려 넣기 위해서..


C#에서는 Random 클래스를 제공한다.

기본적인 사용법은 아래와 같다.

Random r = new Random();
randomNumber = r.Next(1, 11);

간단하다, Random 타입 객체를 생성하고, Next 메서드를 호출하여 난수를 얻는다.

Next 메서드의 인자로는 최소값, 최대값을 준다.

(*최대값은 원하는 최대값 + 1값을 넘긴다. 1부터 10까지 중 난수를 얻고자 한다면,

Next(1, 11); 과 같이...)


자, 한번 코드로 확인해보자.

우선 간단히 WPF로 프로젝트를 하나 만들자(WPF가 아닌 윈폼도 전혀 무방하다..).

화면엔 간단히 리스트 박스 하나로 꽉 채우자. 아래 처럼.

너무 썰렁한가..


코드도 썰렁하다.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(2);
            timer.Tick += timer_Tick;
            timer.Start();
        }

        void timer_Tick(object sender, EventArgs e)
        {            
            string logString = string.Empty;

            for (int i = 0; i < 10; i++)
            {
                Random r = new Random();
                logString += (r.Next(1, 11).ToString() + " ");
            }
            
            this.resultLog.Items.Add(logString);
        }
    }

타이머를 하나 두어 반복적으로 실행시키고, 타이머 핸들러에서는 매 주기마다

1부터 10까지 수중 하나를 10개씩 만들어 리스트 박스에 표시하도록 했다.

어떤까.. 뭐 간단한 코드이니 더 들여다 볼것도 없다.

실행해보자.

...

이게 뭐냐.. 주기마다 난수가 생성되긴 하지만, 매 주기마다 반복문을 돌며 난수가 아닌 동일한 수가 만들어졌다.

분명, for문을 통해 반복적으로 Random 객체를 새로 생성해서 난수를 얻는데, 왜 10번 모두 같단 말인가..

(만일 주기마다 10개가 아닌 1개씩만 찍어보기로 했다면, 위 코드와 결과에서 어떤 이상함도 발견하지 못했을 수도 있다.)

...

이유는 간단하다. Random 객체는 시간을 seed로 삼아 생성된다. 위 코드에서는 반복문을 열라 빨리 돌면서,

거의 동시간(?)대, 즉 시간적으로 큰 차이를 보이지 않는 순간에 모두 생성되어, 동일한 구간의 난수를 만들어 내니,

10번의 결과가 모두 같은 것이었다.


그럼 이를 어떻게 해결하는가..

위 문제의 원인대로, Random 객체를 서로 비슷하지 않은 시간에 생성하면 될 것이다.

그러나, 위와 같은 시나리오에서는 Random 객체를 난수가 필요할 때마다 매번 생성시킬 필요는 없다.

처음 하나 만들어 두고 주구장창 써도 괜찮을 것이고, 오히려 이편이 매번 같은 시간대에 동일한 seed로 Random 객체를

생성시키지도 않을 것이다.


어디 한번 해보자.

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            DispatcherTimer timer = new DispatcherTimer();
            timer.Interval = TimeSpan.FromSeconds(2);
            timer.Tick += timer_Tick;
            timer.Start();
        }

        void timer_Tick(object sender, EventArgs e)
        {
            Random r = new Random();
            string logString = string.Empty;
            
            for (int i = 0; i < 10; i++)
                logString += (r.Next(1, 11).ToString() + " ");
            
            this.resultLog.Items.Add(logString);
        }
    }
}

Random 객체 생성 부분을 for문 밖으로 빼냈다.

(아예, 멤버로 선언하고, timer 핸들러 밖으로 빼내도 무방할 것이다. 타이머의 주기가 엄청 빨라지면 똑같이 동일한 문제가 발생할 수도 있으니...)


자, 이제 실행해보자.

뭐 이정도면 충분히 골고루다. 충분히 어지럽다... 끝.


Posted by dngchn