제네릭 클래스는 특정 데이터 형식과 관련이 없는 작업을 캡슐화합니다. 제네릭 클래스는 연결된 목록, 해시 테이블, 스택, 큐, 트리 등의 컬렉션에 가장 일반적으로 사용됩니다. 컬렉션에서 항목을 추가하고 제거하는 등의 작업은 저장되는 데이터의 형식과 관계없이 기본적으로 동일한 방식으로 수행됩니다.

컬렉션 클래스를 필요로 하는 대부분의 시나리오에서는 .NET 클래스 라이브러리에서 제공하는 컬렉션 클래스를 사용하는 것이 좋습니다. 이러한 클래스 사용에 대한 자세한 내용은 .NET의 제네릭 컬렉션을 참조하세요.

일반적으로 기존의 구체적인 클래스로 시작하여 일반성과 편의성의 균형이 맞을 때까지 형식을 하나씩 형식 매개 변수로 변경하여 제네릭 클래스를 만듭니다. 고유한 제네릭 클래스를 만들 때 중요한 고려 사항은 다음과 같습니다.

  • 형식 매개 변수로 일반화할 형식

    일반적으로 매개 변수화할 수 있는 형식이 많을수록 코드의 유용성과 재사용 가능성이 향상됩니다. 그러나 지나친 일반화는 다른 개발자가 읽거나 이해하기 어려운 코드를 만들어낼 소지가 있습니다.

  • 형식 매개 변수에 적용할 제약 조건(형식 매개 변수에 대한 제약 조건 참조)

    필요한 형식을 처리할 수 있는 범위 내에서 최대한 많은 제약 조건을 적용하는 것이 좋습니다. 예를 들어 제네릭 클래스를 참조 형식으로만 사용하려는 경우에는 클래스 제약 조건을 적용합니다. 이렇게 하면 클래스를 값 형식으로 잘못 사용하는 것을 막을 수 있고, as 연산자를 T에 적용하여 null 값 여부를 확인할 수 있습니다.

  • 제네릭 동작을 기본 클래스와 서브클래스로 분할할지 여부

    제네릭 클래스는 기본 클래스가 될 수 있으므로 제네릭이 아닌 클래스에 적용되는 디자인 고려 사항이 동일하게 적용됩니다. 자세한 내용은 이 항목의 뒷부분에서 설명하는 제네릭 기본 클래스에서 상속하는 데 대한 규칙을 참조하세요.

  • 제네릭 인터페이스를 하나 이상 구현할지 여부

    예를 들어 제네릭 기반 컬렉션에 항목을 만드는 데 사용될 클래스를 디자인할 경우 클래스 형식이 T인 IComparable<T>와 같은 인터페이스를 구현해야 할 수 있습니다.

간단한 제네릭 클래스의 예제를 보려면 제네릭 소개를 참조하세요.

형식 매개 변수와 제약 조건에 대한 규칙은 제네릭 클래스 동작, 특히 상속과 멤버 접근성에 몇 가지 영향을 줍니다. 계속하려면 몇 가지 용어를 이해하고 있어야 합니다. 제네릭 클래스 Node<T>,의 경우 클라이언트 코드는 형식 인수를 지정하여 폐쇄형 생성 형식(Node<int>)을 만들어 클래스를 참조할 수 있습니다. 또는 제네릭 기본 클래스를 지정하는 경우와 같이 형식 매개 변수를 지정하지 않고 개방형 생성 형식(Node<T>)을 만들 수 있습니다. 제네릭 클래스는 구체적인 클래스, 폐쇄형 생성 클래스 또는 개방형 생성 기본 클래스에서 상속할 수 있습니다.

C#
class BaseNode { }
class BaseNodeGeneric<T> { }

// concrete type
class NodeConcrete<T> : BaseNode { }

//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }

//open constructed type 
class NodeOpen<T> : BaseNodeGeneric<T> { }

제네릭이 아닌 구체적인 클래스는 폐쇄형 생성 기본 클래스에서는 상속할 수 있지만 개방형 생성 클래스 또는 형식 매개 변수에서는 상속할 수 없습니다. 이는 런타임에 클라이언트 코드에서 기본 클래스를 인스턴스화할 때 필요한 형식 인수를 제공할 수 없기 때문입니다.

C#
//No error
class Node1 : BaseNodeGeneric<int> { }

//Generates an error
//class Node2 : BaseNodeGeneric<T> {}

//Generates an error
//class Node3 : T {}

개방형 생성 형식에서 상속하는 제네릭 클래스에서는 다음 코드와 같이 상속하는 클래스에서 공유하지 않는 모든 기본 클래스 형식 매개 변수에 대해 형식 인수를 제공해야 합니다.

C#
class BaseNodeMultiple<T, U> { }

//No error
class Node4<T> : BaseNodeMultiple<T, int> { }

//No error
class Node5<T, U> : BaseNodeMultiple<T, U> { }

//Generates an error
//class Node6<T> : BaseNodeMultiple<T, U> {} 

개방형 생성 형식에서 상속하는 제네릭 클래스에서는 기본 형식에 대한 제약 조건을 포함하거나 암시하는 제약 조건을 지정해야 합니다.

C#
class NodeItem<T> where T : System.IComparable<T>, new() { }
class SpecialNodeItem<T> : NodeItem<T> where T : System.IComparable<T>, new() { }

제네릭 형식은 다음과 같이 여러 형식 매개 변수와 제약 조건을 사용할 수 있습니다.

C#
class SuperKeyType<K, V, U>
    where U : System.IComparable<U>
    where V : new()
{ }

개방형 생성 형식 및 폐쇄형 생성 형식은 메서드 매개 변수로 사용할 수 있습니다.

C#
void Swap<T>(List<T> list1, List<T> list2)
{
    //code to swap items
}

void Swap(List<int> list1, List<int> list2)
{
    //code to swap items
}

제네릭 클래스에서 인터페이스를 구현하면 이 클래스의 모든 인스턴스를 해당 인터페이스에 캐스팅할 수 있습니다.

제네릭 클래스는 고정적입니다. 즉, 입력 매개 변수에서 List<BaseClass>를 지정하면 List<DerivedClass>를 제공하려고 할 때 컴파일 시간 오류가 발생합니다.

참고 항목

System.Collections.Generic
C# 프로그래밍 가이드
제네릭
열거자의 상태 저장
상속 퍼즐, 1부

VB 단축키 모음

 단축키

실행 내용 

단축키 

실행 내용 

Tab
Shift+Tab
Shift+F2
F8
Ctrl+I
Ctrl+J
Crtl+아래쪽 화살표

들여쓰기
내어쓰기
정의(루틴검색)
한 단계씩 코드 실행
요약 정보
속성/메서드 목록
다음 프로시저의 첫번째 줄로 이동

Crtl+위쪽 화살표
Ctrl+SpaceBar
Ctrl+S
Ctrl+Z
Ctrl+G
Ctrl+Break

 

이전 프로시저의 첫번째 줄로 이동
단어 채우기
저장
실행취소
직접 실행 창
중단

Del
F1
F2
F3
F4
F5
F7
F9
Ctrl+A
Ctrl+C
Ctrl+D
Ctrl+E
Ctrl+F
Ctrl+H
Ctrl+J
Ctrl+K
Ctrl+L
Ctrl+N
Ctrl+O
Ctrl+P
Ctrl+R
Ctrl+T
Ctrl+V
Ctrl+W
Ctrl+X
Ctrl+Y

삭제
선택된 항목에 대한 비주얼 베이직 도움말 보기
개체 찾아보기
다음 찾기
속성 창
시작
코드창으로 이동하기
중단점 설정/해제
전체선택
복사
파일 추가
메뉴 편집기
찾기
바꾸기
맨앞으로 가져오기
맨뒤로
호출 스택
새 프로젝트
프로젝트 열기
인쇄
프로젝트 탐색기
구성 요소
붙여넣기
조사식 편집
잘라내기
현재줄 잘라내기

Ctrl+F5
Ctrl+F8
Ctrl+F9
Crtl+DEL
Crtl+End
Crtl+Home
Crtl+Page Up
Crtl+Page down
Crtl+오른쪽 화살표
Crtl+왼쪽 화살표
Shift+F3
Shift+F4
Shift+F5
Shift+F7
Shift+F8
Shift+F9
Shift+F10
Shift+오른쪽 화살표
Shift+왼쪽 화살표
Shift+아래쪽 화살표
Alt+Q
Ctrl+Shift+I
Ctrl+Shift+J
Ctrl+Shift+F2
Ctrl+Shift+F8
Ctrl+Shift+F9

전체 컴파일후 시작
커서까지 실행
다음 문 설정
한 단어만 지우기
해당 모듈의 끝으로 이동
해당 모듈의 처음으로 이동
이전 프로시저 선언으로 가기
다음 프로시저 선언으로 가기 
한 단어만큼 오른쪽으로 이동
한 단어만큼 왼쪽으로 이동
이전 찾기
속성 페이지
다시 시작
개체
프로시저 단위 실행
간략한 조사식
오른쪽 마우스 버튼 클릭한것과 동일한 효과
오른쪽으로 한 단어 더 선택하기
왼쪽으로 선택된 한 단어 해제하기
위로 한 줄 더 선택하기/지우기
종료
매개 변수 정보
상수 목록
이전 위치
프로시저 나가기
모든 중단점 지우기

 

키보드로 컨트롤 다루기

 SHIFT + RIGHT ARROW

 컨트롤의 폭(width)을 증가시킵니다.

 CONTROL + RIGHT ARROW

 컨트롤을 오른쪽으로 움직입니다.

 SHIFT + LEFT ARROW

 컨트롤의 폭(width)을 감소시킵니다.

 CONTROL + LEFT ARROW

 컨트롤을 왼쪽으로 움직입니다.

 SHIFT + DOWN ARROW

 컨트롤의 높이(height)을 증가시킵니다.

 CONTROL + DOWN ARROW

 컨트롤을 아래로 움직입니다.

 SHIFT + UP ARROW

 컨트롤의 높이(height)을 감소시킵니다.

 CONTROL + UP ARROW

 컨트롤을 위로 움직입니다.


'Knowledge > Visual Basic 6+' 카테고리의 다른 글

[VB6] 상수 선언  (0) 2018.05.09
[VB6] 전역변수 선언 (Not Global keyword)  (0) 2018.04.02
[VB6] Replace Function - Visual Basic 6.0  (0) 2018.04.02
Visual Basic 문법  (0) 2018.03.27
[VB6] What is Me.Caption  (0) 2018.03.26

http://www.masque.kr/free/383086



 

스레드 - 컨트롤Invoke(동기), BeginInvoke(비동기)

 

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
public Form1()
{
    InitializeComponent();
 
    var thread = new Thread(OneThread);
    thread.Name = "OneThread";
    thread.Start();
}
 
private void OneThread()
{
    var i = 0;
    while (true)
    {
        try
        {
            // Thread Safe
            // 동기
            //Invoke(new TextHandler(UpdateButtonText), new object[] { "스레드" + i });
 
            // 비동기
            BeginInvoke((MethodInvoker)(() => { button1.Text = "스레드" + i; button1.Update(); }));
            //BeginInvoke((MethodInvoker)delegate { button1.Text = "스레드" + i; button1.Update(); });
            //BeginInvoke((MethodInvoker)(() => UpdateButtonText("스레드" + i )));
            //BeginInvoke(new TextHandler(UpdateButtonText), "스레드" + i);
            //BeginInvoke(new TextHandler(UpdateButtonText), new object[] { "스레드" + i });
 
            // Cross Thread
            //button1.Text = "테스트";
            //button1.Update();
        }
        catch (Exception e)
        {
            // 크로스 스레드 작업이 잘못되었습니다.
            // button1' 컨트롤이 자신이 만들어진 스레드가 아닌 스레드에서 액세스되었습니다.
        }
 
        i++;
    }
}
 
//public delegate void TextHandler(String text);
//private void UpdateButtonText(String text)
//{
//    button1.Text = text;
//    button1.Update();
//}

참고
 - https://msdn.microsoft.com/ko-kr/library/ms171728%28v=vs.110%29.aspx
  

 - http://stackoverflow.com/questions/253138/anonymous-method-in-invoke-call
버튼과 같은 스레드로 접근을 하여 크로스 스레드를 피할수 있다

성능면에서 동기는 기다리고, 비동기는 끝날때 호출되어 자원을 효율적으로 사용한다.

InvokeRequired는 성능과는 무관하고, 같은 스레드인지 확인용이다.

 


동기 : 보통의 프로그래밍환경, 한 라인씩 실행한다.

비동기 : 스레드를 생성하여, 실행한다. 모두 처리하지 않아도 다음라인으로 넘어간다.

           서버와 통신할때 기다릴 필요없이 다음 라인을 실행할수 있다.

 

UI.BeginInvoke : 비동기로 대리자를 실행한다. 스레드는 UI와 동일, 스레드충돌해결
대리자.BeginInvoke : 비동기로 자신을 실행한다. 스레드풀의 스레드를 사용

BeginInvoke보이지 않으면, "고급 멤버 숨기기"를 체크 해제 한다

 

동기를 해야 할때

하늘에서 운석이 떨어진다. 지구종말 시간이 가까워 졌다.

여자친구와 헤어졌다. 인생 허무하다.

블리자드가 망해서, 배틀넷이 정지되고, 계정이 잠겼다.

MB의 잘못이 모두 드러나고, 사형집행이 결정되었다. 장소는 서울시청앞

 

비동기를 해야 할때

로그인등, 일반적인 프로젝트 상황

로그인은 동기로 해야 할거 같다고 생각했는데, 실제로는 동기가 되면, 대화상자가 응답없음이 되어 버린다.

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public delegate void TextHandler(String text);
private void UpdateButtonText(String text)
{
    if (this.button1.InvokeRequired)
    {
        this.Invoke(new TextHandler(UpdateButtonText), new object[] { text });
    }
    else
    {
        this.button1.Text = text;
        this.button1.Update();
    }
}
// InvokeRequired예제, 동기사용, 비권장

참고 https://msdn.microsoft.com/ko-kr/library/ms171728(v=vs.110).aspx


'Knowledge' 카테고리의 다른 글

[UWP] MetroLog 추가하기  (0) 2018.04.21
제네릭 클래스(C# 프로그래밍 가이드)  (0) 2018.04.20
C# 5.0 || await || async  (0) 2018.04.19
C# version  (0) 2018.04.19
Mutex 클래스  (0) 2018.04.19
C# 5.0 await 

C# 5.0부터 비동기 프로그래밍을 보다 손쉽게 지원하기 위하여 async와 await라는 C# 키워드가 추가되었다. async는 컴파일러에게 해당 메서드가 await를 가지고 있음을 알려주는 역활을 하고, await는 awaitable 클래스(즉 GetAwaiter() 라는 메서드를 갖는 클래스)이면 함께 사용되어 awaitable 객체가 완료되기를 기다리는 역활을 한다. 여기서 중요한 점은 await는 UI 쓰레드가 정지되지 않고 메시지 루프를 계속 돌 수 있도록 필요한 코드를 컴파일러가 자동으로 추가한다는 점이다. 메시지 루프가 계속 돌게 만든다는 것은 마우스 클릭이나 키보트 입력, 화면 페인팅 등을 계속 처리할 수 있다는 것을 의미한다. await는 해당 Task가 끝날 때까지 기다렸다가 완료 후, 바로 다음 실행문부터 실행을 계속하는데, 이 때의 쓰레드는 디폴트로 await를 호출하기 이전의 실행 쓰레드가 된다. 즉 UI Thread에서 await를 호출하였다면 await 다음 문장들은 UI 쓰레드에서 실행된다. (Note: 만약 task.ConfigureAwait(false)를 실행하면 쓰레드풀의 쓰레드로 실행된다) 

예제

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

namespace awaitTest
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void btnRun_Click(object sender, EventArgs e)
        {
            RunIt();
        } 

        // async를 붙인다.
        private async void RunIt()
        {
            // 긴 계산을 하는 메서드 비동기로 호출
            Task<double> task = Task<double>.Factory.StartNew(() => LongCalc(10));

            // Task가 끝나기를 기다림. 하지만 UI 쓰레드는 Block되지 않는다.
            await task;

            // Task가 끝난 다음 아래 UI 컨트롤의 값 갱신
            // 이 문장은 UI 쓰레드에서 실행되므로 Invoke()가 필요 없다.
            textBox1.Text = task.Result.ToString();
        }

        double LongCalc(double r)
        {
            // 3초간 긴 계산
            Thread.Sleep(3000);

            return 3.14 * r * r;
        }

    }
}




The biggest problem when dealing with C#'s version numbers is the fact that it is not tied to a version of the .NET Framework, which it appears to be due to the synchronized releases between Visual Studio and the .NET Framework.

The version of C# is actually bound to the compiler, not the framework. For instance, in Visual Studio 2008 you can write C# 3.0 and target .NET Framework 2.0, 3.0 and 3.5. The C# 3.0 nomenclature describes the version of the code syntax and supported features in the same way that ANSI C89, C90, C99 describe the code syntax/features for C.

Take a look at Mono, and you will see that Mono 2.0 (mostly implemented version 2.0 of the .NET Framework from the ECMA specifications) supports the C# 3.0 syntax and features.

    • C# 1.0 with Visual Studio.NET

    • C# 2.0 with Visual Studio 2005

    • C# 3.0 with Visual Studio 2008

    • C# 4.0 with Visual Studio 2010

    • C# 5.0 with Visual Studio 2012

    • C# 6.0 with Visual Studio 2015

    • C# 7.0 with Visual Studio 2017






    These are the versions of C# known about at the time of this writing:

    • C# 1.0 released with .NET 1.0 and VS2002 (January 2002)
    • C# 1.2 (bizarrely enough); released with .NET 1.1 and VS2003 (April 2003). First version to call Dispose on IEnumerators which implemented IDisposable. A few other small features.
    • C# 2.0 released with .NET 2.0 and VS2005 (November 2005). Major new features: generics, anonymous methods, nullable types, iterator blocks
    • C# 3.0 released with .NET 3.5 and VS2008 (November 2007). Major new features: lambda expressions, extension methods, expression trees, anonymous types, implicit typing (var), query expressions
    • C# 4.0 released with .NET 4 and VS2010 (April 2010). Major new features: late binding (dynamic), delegate and interface generic variance, more COM support, named arguments, tuple data type and optional parameters
    • C# 5.0 released with .NET 4.5 and VS2012 (August 2012). Major features: async programming, caller info attributes. Breaking change: loop variable closure.
    • C# 6.0 released with .NET 4.6 and VS2015 (July 2015). Implemented by Roslyn. Features: initializers for automatically implemented properties, using directives to import static members, exception filters, element initializers, await in catch and finally, extension Add methods in collection initializers.
    • C# 7.0 released with .NET 4.7 and VS2017 (March 2017) Major new features: tuples, ref locals and ref return, pattern matching (including pattern-based switch statements), inline outparameter declarations, local functions, binary literals, digit separators, and arbitrary async returns.
    • C# 7.1 released with VS2017 v15.3 (August 2017) Minor new features: async maintuple member name inferencedefault expressionpattern matching with generics.
    • C# 7.2 released with VS2017 v15.5 (November 2017) Minor new features: private protected access modifierSpan<T>, aka interior pointer, aka stackonly structeverything else.

    There is no such thing as C# 3.5 - the cause of confusion here is that the C# 3.0 is present in .NET 3.5. The language and framework are versioned independently, however - as is the CLR, which is at version 2.0 for .NET 2.0 through 3.5, .NET 4 introducing CLR 4.0, service packs notwithstanding. The CLR in .NET 4.5 has various improvements, but the versioning is unclear: in some places it may be referred to as CLR 4.5 (this MSDN page used to refer to it that way, for example), but the Environment.Version property still reports 4.0.xxx.

    More detailed information about the relationship between the language, runtime and framework versions is available on the C# in Depth site. This includes information about which features of C# 3.0 you can use when targeting .NET 2.0. (If anyone wants to bring all of the content into this wiki answer, they're welcome to.)

    As of May 3, 2017, the C# Language Team created a history of C# versions and features on their github repo: Features Added in C# Language Versions





    Mutex 클래스 

    Mutex 클래스는 Monitor클래스와 같이 특정 코드 블럭(Critiacal Section)을 배타적으로 Locking하는 기능을 가지고 있다. 단, Monitor클래스는 하나의 프로세스 내에서만 사용할 수 있는 반면, Mutex 클래스는 해당 머신의 프로세스간에서도 배타적 Locking을 하는데 사용된다. Mutex 락킹은 Monitor 락킹보다 약 50배 정도 느리기 때문에 한 프로세스내에서만 배타적 Lock이 필요한 경우는 C#의 lock이나 Monitor 클래스를 사용한다. 아래 예제는 MyClass가 외부 프로세스에서도 사용할 수 있다는 가정하에 배타적 Lock으로 뮤텍스를 사용하는 예이다. (실제로는 한 프로세스 내에서 사용할 경우, Monitor를 사용하는 것이 빠른 방식이다) 

    예제

    using System;
    using System.Threading;
    using System.Collections.Generic;   
    
    namespace MultiThrdApp
    {
        class Program
        {
            static void Main(string[] args)
            {
                // 2개의 쓰레드 실행
                Thread t1 = new Thread(() => MyClass.AddList(10));
                Thread t2 = new Thread(() => MyClass.AddList(20));
                t1.Start();
                t2.Start();
    
                // 2개의 쓰레드 실행완료까지 대기
                t1.Join();
                t2.Join();
    
                // 메인쓰레드에서 뮤텍스 사용
                using (Mutex m = new Mutex(false, "MutexName1"))
                {
                    // 뮤텍스를 취득하기 위해 10 ms 대기
                    if (m.WaitOne(10))
                    {
                        // 뮤텍스 취득후 MyList 사용
                        MyClass.MyList.Add(30);
                    }
                    else
                    {
                        Console.WriteLine("Cannot acquire mutex");
                    }
                }
    
                MyClass.ShowList();
            }
        }
    
        public class MyClass
        {
            // MutexName1 이라는 뮤텍스 생성
            private static Mutex mtx = new Mutex(false, "MutexName1");
    
            // 데이타 멤버
            public static List<int> MyList = new List<int>(); 
    
            // 데이타를 리스트에 추가
            public static void AddList(int val)
            {
                // 먼저 뮤텍스를 취득할 때까지 대기
                mtx.WaitOne();
    
                // 뮤텍스 취득후 실행 블럭
                MyList.Add(val);         
       
                // 뮤텍스 해제
                mtx.ReleaseMutex();
            }
    
            // 리스트 출력
            public static void ShowList()
            {
                MyList.ForEach(p => Console.WriteLine(p));
            }
        }
    }
    
    



    프로세스간 Mutex 활용 

    Mutex 활용에 적합한 예로서 흔히 한 머신 내에서 오직 한 응용프로그램(Application)만이 실행되도록 하는 테크닉을 든다. 한 컴퓨터 내 한 프로세스만 뜨게 하기 위해 고유의 Mutex명을 지정할 필요가 있는데, 일반적으로 이를 위해 GUID (globally unique identifier)를 사용한다. 처음 프로세스가 먼저 Mutex를 획득하면 다른 프로세스는 Mutex를 획득할 수 없기 때문에 오직 하나의 프로그램만 머신 내에서 실행되는 것이다. 

    예제

    class Program
    {
        static void Main()
        {
            // Unique한 뮤텍스명을 위해 주로 GUID를 사용한다.
            string mtxName = "60C3D9CA-5957-41B2-9B6D-419DC9BE77DF";
    
            // 뮤텍스명으로 뮤텍스 객체 생성 
            // 만약 뮤텍스를 얻으면, createdNew = true
            bool createdNew;
            Mutex mtx = new Mutex(true, mtxName, out createdNew);
    
            // 뮤텍스를 얻지 못하면 에러
            if (!createdNew)
            {
                Console.WriteLine("에러: 프로그램 이미 실행중");
                return;
            }
    
            // 성공하면 본 프로그램 실행
            MyApp.Launch();
        }
    }

    'Knowledge' 카테고리의 다른 글

    C# 5.0 || await || async  (0) 2018.04.19
    C# version  (0) 2018.04.19
    Working With Thread Local Storage (TLS) in C#  (0) 2018.04.18
    Cross Thread Operations in C#  (0) 2018.04.18
    Aborting Thread Vs Cancelling Task  (0) 2018.04.18


    Thread-local storage (TLS) is a computer programming method that uses static or global memory local to a thread.  All threads of a process share the virtual address space of the process. The local variables of a function are unique to each thread that runs the function. However, the static and global variables are shared by all threads in the process. With thread local storage (TLS), you can provide unique data for each thread that the process can access using a global index. One thread allocates the index, which can be used by the other threads to retrieve the unique data associated with the index.

    In the .NET Framework version 4, you can use the System.Threading.ThreadLocal<T> class to create thread-local objects.

    System.Threading.ThreadLocal<T>

    where T Specifies the type of data stored per-thread. The ThreadLocal(Of T) type exposes the following members.

    Constructors

    • ThreadLocal(T)- The constructer used to initialize the ThreadLocal(T) instance.
    • ThreadLocal(T)(Tfunc(T))- This constructer is used to initialize the ThreadLocal(Of T)instance with the specified valueFactory function.

    Properties

    • Value- This property is used to get or set the value of this instance for the current thread.
    • IsValueCreated- This property is used to get whether a value is initialized on the current thread.

    Methods

    • Dispose()- This method is used to release all resources used by the current instance of the ThreadLocal(Of T) class.
    • Equals(Object)- This method determines whether the specified Object is equal to the current Object. (Inherited from Object.)
    • Finalize()- This method is used to release the resources used by this ThreadLocal(Of T) instance. (Overrides Object.Finalize.)
    • GetHashCode()- This method serves as a hash function for a particular type. (Inherited from Object.)
    • GetType()- This method gets the Type of the current instance. (Inherited from Object.)
    • MemberwiseClone()- This method is uesd to create a shallow copy of the current Object. (Inherited from Object.)
    • ToString()- This method is uesd to create and return a string representation of this instance for the current thread. (Overrides Object.ToString.)
       

    The .NET Framework provides dynamic data slots that are unique to a combination of thread and application-domain. There are two types of data slots: named slots and unnamed slots. Both are implemented by using the LocalDataStoreSlot structure.

    • To create a named data slot, use the Thread.AllocateDataSlot or Thread.GetNamedDataSlot method. To get a reference to an existing named slot, its name to the GetNamedDataSlot method.
    • To create an unnamed data slot, use the Thread.AllocateDataSlot method.

    For both named and unnamed slots, use the Thread.SetData andThread.GetData methods to set and retrieve the information in the slot. These are static methods that always act on the data for the thread that is currently executing them.

    Named slots can be convenient, because you can retrieve the slot when you need it by ing its name to the GetNamedDataSlot method, instead of maintaining a reference to an unnamed slot. However, if another component uses the same name for its thread-relative storage and a thread executes code from both your component and the other component, the two components might corrupt each other's data. (This scenario assumes that both components are running in the same application domain, and that they are not designed to share the same data.)

    The following example shows how to use ThreadLocal(T):

    using System;
    using System.Threading;
    namespace ThreadLocalStorage
    {
        class akshay
        {
                 public static void WriteError()
            {
                Console.WriteLine("Error number = " +Thread.GetData(Thread.GetNamedDataSlot("ErrNo")));
                Console.WriteLine("Error source = " +Thread.GetData(Thread.GetNamedDataSlot("ErrSource"))
            } 
            public static void SetError()
            {
                Random r = new Random();
                Thread.SetData(Thread.GetNamedDataSlot("ErrNo"), r.Next(100));
                Thread.SetData(Thread.GetNamedDataSlot("ErrSource"),Thread.CurrentThread.Name);
                WriteError();
            }
            public static void Main()
            {
                Thread.AllocateNamedDataSlot("ErrNo");
                Thread.AllocateNamedDataSlot("ErrSource");
                Thread th2 = new Thread(new ThreadStart(SetError));
                th2.Name = "t2";
                th2.Start();
                Thread th3 = new Thread(new ThreadStart(SetError));
                th3.Name = "t3";
                th3.Start();
                Thread.FreeNamedDataSlot("ErrNo");
                Thread.FreeNamedDataSlot("ErrSource");
                Console.Read();
            }
        }
    }

    Output 

    img1.jpg

    We can create thread local storage in the managed environment. Depending on your application, it may be necessary for you to have a static field of a class that is unique for each thread that the class is used in. Doing so is trivially easy in the majority of the cases in C#. If you have a static field that must be thread relative, simply adorn it with the ThreadStaticAttribute attribute. Once you do that, the field will be initialized for each thread that accesses it. Under the covers, each thread is given its own thread relative location to save the value or reference. However, when using references to objects, be careful with your assumptions about the object's creation. The following code shows a pitfall to avoid.

    using System;
    using
     System.Threading;
    namespace ThreadLocalStorage
    {
    public class TLS
        {
            public TLS()
            {
                Console.WriteLine("Creating TLS Class");
            }
         }
     public class TLSFlied
            {
                [ThreadStatic]
                public static TLS Tdata = new TLS();        
            }
      public class EntryPoint
            {
                private static void ThreadFun()
                {
                    Console.WriteLine("Thread {0} starting.....",Thread.CurrentThread.ManagedThreadId);
                    Console.WriteLine("TData for this thread is \"{0}\"",TLSFlied.Tdata);
                    Console.WriteLine("thread {0} exiting",Thread.CurrentThread.ManagedThreadId);
                }
                static void Main()
                {
                    Thread t1 = new Thread(new ThreadStart(EntryPoint.ThreadFun));
                    Thread t2 = new Thread(new ThreadStart(EntryPoint.ThreadFun));
                    t1.Start();
                    t2.Start();
                    Console.Read();
                }
            }
    }

    Output

    img2.jpg

    Resources

    Thread- local storage of data in .NET
    Use Thread Local Storage to Thread Specific Data
    Storing Data in C#
    Windows Azure - Local Storage Feature



    'Knowledge' 카테고리의 다른 글

    C# version  (0) 2018.04.19
    Mutex 클래스  (0) 2018.04.19
    Cross Thread Operations in C#  (0) 2018.04.18
    Aborting Thread Vs Cancelling Task  (0) 2018.04.18
    Task And Thread In C#  (0) 2018.04.18



    Well, if you are working with threads and want to access a control that is created in another thread then you will always face a problem saying that:

    pic1.jpg

    To solve the tiny problem, Here is the solution

    First of all start the thread and assign it a method you want to execute it on.

    Remember, the method you want to assign to the thread should not contain any parameters.

    2.JPG

    Now here is the RunLoop method

    3.JPG

    In the first line we get to know whether the control we want to access is created on the same thread or another thread. If it's created on another thread then it will execute the second line.

    In the second line we create a MethodInvoker with the name of AssignMethodToControl which is a delegate that executes a method that has no parameters.

    In the third line we invoke the MethodInvoker to the Control.

    We have achieved our aim so the method will re-execute and it will loop through condition.

    pic3.jpg
     

    Mohammad Ajmal Amirzad

    Mohammad Ajmal Amirzad

    From Afghanistan is software developer since 2008, Currently working with Chemonics International Inc as System Developer having experience in developing softwares for governmental, educational, healthcare, pharmaceutica... Read more

    http://www.softlogicaf.com/


    'Knowledge' 카테고리의 다른 글

    Mutex 클래스  (0) 2018.04.19
    Working With Thread Local Storage (TLS) in C#  (0) 2018.04.18
    Aborting Thread Vs Cancelling Task  (0) 2018.04.18
    Task And Thread In C#  (0) 2018.04.18
    [.NET] 텍스트 파일에서 읽기  (0) 2018.04.15

    This post is based on one of the questions I answered on StackOverflow, in which the questioner wants to cancel the task when it's taking too long to respond, i.e., taking too much time in execution and returning the result. But, when I tried to provide the answer to that question, I found there is no direct way to cancel the task when making the call to Web Service or making the call to Database to get the data via third-party library ( XenAPI in my case) which is hanging up the application and not allowing it to proceed. To understand this, have a look at the below code.

    1. Var task = Task.Factory.StartNew(()=> CallWebServiceandGetData());  

    The above line of code is creating the task which is making calls to the webservice to get the data. Now, the developer wants to write a code in such a way that if the task takes more than 10 seconds, it gets canceled. However, in TPL library, there is no way to cancel the task, i.e., there is no direct method or there is no other way to make this task cancel because task.Cancel() or task.Abort() like methods do not exist in TPL. The below post is about how a developer can really abort a task.

    Aborting thread vs Cancelling task

    What is the difference between aborting thread and cancelling task.

    Aborting thread 

    System.Treading is a library provided for threading prior to TPL library. (Just to note - old System.Threading is still part of .NET framework but TPL provides more control on Task which is wrapper around thread). In this library, to abort thread there is a method called Abort() available. With the help of this method, a developer can ask execution environment (CLR) to abort the thread. Below is an example code for the same.

    1. Thread newThread = new Thread(() => Console.WriteLine("Test"));  
    2. newThread.Start();  
    3. Thread.Sleep(1000);//sleeping main thread  
    4. newThread.Abort();main thread aborting newly created thread.  

    Cancelling Task 

    In the newer library, TPL (System.Threading.Tasks), there is no direct method which cancels or aborts the underlying thread. But there is a way to cancel a task by using CancellationTokenSource class which allows you to pass the CancellationToken as one of the input parameters when you create the task. (more on this is discussed below). Below is the code for cancelling the task.

    1. var source = new CancellationTokenSource();  
    2.           CancellationToken token = source.Token;  
    3.           Task.Factory.StartNew(() => {   
    4.             for(int i=0;i< 10000;i++)  
    5.             {  
    6.                 Console.WriteLine(i);  
    7.                 if (token.IsCancellationRequested)  
    8.                     token.ThrowIfCancellationRequested();  
    9.             }  
    10.           }, token);  
    11.           source.CancelAfter(1000);  

    So, the above code makes use of CancellationTokenSource and CancellationToken provided by it. In the above code, CancellationTokenSource calls the method CancelAfter which sets taskCancellation flag. This flag is watched inside delegate via IsCancellationRequested property on CancellationToken. And once it sees in the for loop that IsCancellationRequested flag is true, it calls the ThrowIfCancellationRequested() method and cancels the thread.

    So, in simple terms, abort thread allows the developer to abort executing thread and CancellationToken in new TPL library does the same thing, which is called cancelation of task. So basically, newer(TPL) and older(Threading) have different way to cancel/abort thread.

    But one of the major differences between Abort() thread and Cancel task is that Abort() can leave application in an inconsistent state ( on Abort(), the system immediately aborts the thread, not allowing you to perform any operation to put application in consistent state ), especially when doing file operation or doing create/update operation , so it is better to take care when aborting thread or writing code in such a way that the application remains in consistent state. That is one of the reasons TPL come up with Cancellation mechanism, so those who write the code can watch cancellation flag and if it gets true, then they can write the code to put application in consistence state.

    Returning to problem of Cancelling task

    By reading the above section of “Cancellation Task”, one can say there is a provision to cancel task which in turn cancels the thread also and puts the system in consistent state, so it’s a better approach. But, if we now go back to the scenario where a task is created to fetch the data from webService or Database which is taking too much long time, the code will be like below with cancellation mechanism.

    1. var source = new CancellationTokenSource();  
    2. CancellationToken token = source.Token;  
    3. Task.Factory.StartNew(() => {   
    4.     try  
    5.     {  
    6.         //below is third party library(XenAPI) method   
    7.         HTTP_actions.put_import(…//parameter of method);  
    8.         //instead of this there can be database call to get data  
    9.         //which takes too much time   
    10.     }  
    11.     catch (HTTP.CancelledException exception)  
    12.     {  
    13.     }  
    14.     //execution never comes here till above method get complete             
    15.     if (token.IsCancellationRequested)  
    16.         token.ThrowIfCancellationRequested();  
    17.                 
    18. }, token);  
    19. source.CancelAfter(1000);  

    In the above scenario, once the call is made to API method, it never comes back, so control of execution will not return to the application and the code which checks cancellation never get executed till the call returns. Which means the Task does not get cancelled even after 1000 ms and its cancellation flag is set to true for cancellation of task.

    Above scenario is based on Third party API so it might be difficult to understand context. For easy understanding, let us have a look at the below code (just one change here, TaskCompletionSource is used to wrap the underlying task).

    1. static Task<string> DoWork(CancellationToken token)  
    2.         {  
    3.             var tcs = new TaskCompletionSource<string>();  
    4.   
    5.             //comment this whole this is just used for testing   
    6.             Task.Factory.StartNew(() =>  
    7.             {  
    8.                 //Simulate work (usually from 3rd party code)  
    9.                 for (int i = 0; i < 100000; i++)  
    10.                     Console.WriteLine("value" + i);  
    11.   
    12.               //execution never comes here till above for loop or          
    13.               //may be long execution /computation get completed             
    14.                if (token.IsCancellationRequested)  
    15.                     token.ThrowIfCancellationRequested();  
    16.   
    17.                 Console.WriteLine("Task finished!");  
    18.             },token);  
    19.             tcs.SetResult("Completed");  
    20.             return tcs.Task;  
    21.         }  
    22.   public static void Main()  
    23.         {  
    24.             var source = new CancellationTokenSource();  
    25.             CancellationToken token = source.Token;  
    26.             DoWork(token);  
    27.             source.CancelAfter(1000);  
    28.             Console.ReadLine();  
    29. }  

    In the above code, instead of third-party code, I replaced it with the For loop (or consider long calculation task). Now, when execution is going on, the application cannot get a chance to read the cancellation flag which is set up by main thread i.e. from the main method. So, the application is not able to cancel the task until computation is over and control reaches the  point where cancellation flag check is done.

    In both of the scenarios, the major problem is when log computation or long call is going on, the application cannot cancel the task. The Cancellation mechanism provided in TPL does not work and there, we need a solution to cancel this task another way.

    Solution code is,

    1. class Program  
    2. {  
    3.     //capture request running that , which need to be cancel in case  
    4.     // it take more time   
    5.     static Thread threadToCancel = null;  
    6.     static async Task<string> DoWork()  
    7.     {  
    8.         var tcs = new TaskCompletionSource<string>();  
    9.         //comment this whole this is just used for testing   
    10.         await Task.Factory.StartNew(() =>  
    11.         {  
    12.             //Capture the thread  
    13.             threadToCancel = Thread.CurrentThread;  
    14.             //Simulate work (usually from 3rd party code)  
    15.             for (int i = 0; i < 100000; i++)  
    16.                  Console.WriteLine("value" + i);  
    17.            Console.WriteLine("Task finished!");  
    18.         });  
    19.         tcs.SetResult("Completed");  
    20.         return tcs.Task.Result;  
    21.     }  
    22.   
    23.     public static void Main()  
    24.     {  
    25.         var source = new CancellationTokenSource();  
    26.         CancellationToken token = source.Token;  
    27.         DoWork();  
    28.         //another task check for cancellation flag  
    29.         //cancels long running task by calling thread abort method   
    30.         Task.Factory.StartNew(() =>  
    31.         {  
    32.             while (true)  
    33.             {  
    34.                 if (token.IsCancellationRequested && threadToCancel != null)  
    35.                 {  
    36.                     threadToCancel.Abort();//abort long running thread  
    37.                     Console.WriteLine("Thread aborted");  
    38.                     return;  
    39.                 }  
    40.             }  
    41.         });  
    42.         //here 1000 can be replace by miliseconds after which you want to   
    43.         // abort thread which calling your long running method   
    44.         source.CancelAfter(1000);  
    45.         Console.ReadLine();  
    46.     }  
    47. }  

    Comments in the code explain most of the things but let's go into detail about how it’s going to work. Following are the changes in code.

    1. Async/await used to make DoWork method asynchronous
    2. threadToCancel variable in code stores the reference of the thread of underlying Task by calling Thread.CurrentThread. This variable allows to cancel the thread of Task.
    3. One more Task gets created in main which keeps checking the Cancellation flag which is setup by source.CancelAfter(1000); after 1000 miliseconds.
    4. Task created in Main is running While loop with true, which keeps checking Cancellation flag true or not, once it gets true, this task executes the method threadToCancel.Abort(); to abort the underlying thread of long running task.

    So, the real magic part in code is the reference to the underlying thread stored via Thread.CurrentThread. And separate tasks run in the main method to abort long-running task threads when cancellation flag sets to true by CancellationSoruce.

    Pranay Rana

    Pranay Rana

     

    Hey, I am Pranay Rana, working as a Devloper in MNC.  Web development in Asp.Net with C# and MS sql server are the experience tools that I have had for the past 8.2 years now.For me def. of programming is : Programm... Read more

    http://pranayamr.blogspot.com


    'Knowledge' 카테고리의 다른 글

    Working With Thread Local Storage (TLS) in C#  (0) 2018.04.18
    Cross Thread Operations in C#  (0) 2018.04.18
    Task And Thread In C#  (0) 2018.04.18
    [.NET] 텍스트 파일에서 읽기  (0) 2018.04.15
    [SQLite3] 튜토리얼 사이트  (0) 2018.04.15


    This article describes the definition and uses of Task And Thread:

    • What is Task?
    • What is Thread?
    • Why do we need Task?
    • Why do we need Thread?
    • How to implement Task
    • How to implement Thread
      Differences between Task And Thread

    What is Task?

    • .NET framework provides Threading.Tasks class to let you create tasks and run them asynchronously.
    • A task is an object that represents some work that should be done.
    • The task can tell you if the work is completed and if the operation returns a result, the task gives you the result.

      c#

    What is Thread?

    • .NET Framework has thread-associated classes in System.Threading namespace.  
    • A Thread is a small set of executable instructions.
      c#

    Why we need Task:

    1. It can be used whenever you want to execute something in parallel. Asynchronous implementation is easy in a task, using’ async’ and ‘await’ keywords.

    Why we need Thread:

    When the time comes when the application is required to perform few tasks at the same time.

    How to create Task:

    1. static void Main(string[] args) {  
    2.     Task < string > obTask = Task.Run(() => (  
    3.         return“ Hello”));  
    4.     Console.WriteLine(obTask.result);  

    How to create Thread:

    1. static void Main(string[] args) {  
    2.     Thread thread = new Thread(new ThreadStart(getMyName));  
    3.     thread.Start();  
    4. }  
    5. public void getMyName() {} 

    Differences Between Task And Thread

    1. The Thread class is used for creating and manipulating a thread in Windows. A Task represents some asynchronous operation and is part of the Task Parallel Library, a set of APIs for running tasks asynchronously and in parallel.
    2. The task can return a result. There is no direct mechanism to return the result from a thread.
    3. Task supports cancellation through the use of cancellation tokens. But Thread doesn't.
    4. A task can have multiple processes happening at the same time. Threads can only have one task running at a time.
    5. We can easily implement Asynchronous using ’async’ and ‘await’ keywords.
    6. A new Thread()is not dealing with Thread pool thread, whereas Task does use thread pool thread.
    7. A Task is a higher level concept than Thread.


    'Knowledge' 카테고리의 다른 글

    Cross Thread Operations in C#  (0) 2018.04.18
    Aborting Thread Vs Cancelling Task  (0) 2018.04.18
    [.NET] 텍스트 파일에서 읽기  (0) 2018.04.15
    [SQLite3] 튜토리얼 사이트  (0) 2018.04.15
    [SQLite3] DataType  (0) 2018.04.15


    디자이너 속성에서는 다룰 수 없어.

    탭 컨트롤러에서 동적으로 제거해라!




    • The tabPage.Enabled seems to be working fine, but is marked as "not to be used":

      This API supports the .NET Framework infrastructure and is not intended to be used directly from your code.
      This member is not meaningful for this control.

      So you should disable the tab page by disabling every control in the tab. See this for instance.

    • Show / hide

      There is an existing tabPage.Visible property but it does not seem to have any effect. Besides, it is also marked as "not to be used", and msdn advises to remove the tab page from the tab control in order to hide it:

      // Hide the tab page
      tabControl.TabPages.Remove(tabPage1);
      // Show the tab page (insert it to the correct position)
      tabControl.TabPages.Insert(0, tabPage1);



    방법: 텍스트 파일에서 읽기(C# 프로그래밍 가이드)

    이 예제에서는 System.IO.File 클래스의 정적 메서드 ReadAllText 및 ReadAllLines를 사용하여 텍스트 파일의 내용을 읽습니다.

    StreamReader를 사용하는 예제는 방법: 텍스트 파일을 한 번에 한 줄씩 읽기를 참조하세요.

    참고

    이 예제에 사용되는 파일은 방법: 텍스트 파일에 쓰기 항목에서 생성되었습니다.

    예제

    C#
    class ReadFromFile
    {
        static void Main()
        {
            // The files used in this example are created in the topic
            // How to: Write to a Text File. You can change the path and
            // file name to substitute text files of your own.
    
            // Example #1
            // Read the file as one string.
            string text = System.IO.File.ReadAllText(@"C:\Users\Public\TestFolder\WriteText.txt");
    
            // Display the file contents to the console. Variable text is a string.
            System.Console.WriteLine("Contents of WriteText.txt = {0}", text);
    
            // Example #2
            // Read each line of the file into a string array. Each element
            // of the array is one line of the file.
            string[] lines = System.IO.File.ReadAllLines(@"C:\Users\Public\TestFolder\WriteLines2.txt");
    
            // Display the file contents by using a foreach loop.
            System.Console.WriteLine("Contents of WriteLines2.txt = ");
            foreach (string line in lines)
            {
                // Use a tab to indent each line of the file.
                Console.WriteLine("\t" + line);
            }
    
            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
    }


    'Knowledge' 카테고리의 다른 글

    Aborting Thread Vs Cancelling Task  (0) 2018.04.18
    Task And Thread In C#  (0) 2018.04.18
    [SQLite3] 튜토리얼 사이트  (0) 2018.04.15
    [SQLite3] DataType  (0) 2018.04.15
    [WUP] SQLite 관련 Tutorial  (0) 2018.04.14



    https://www.tutorialspoint.com/sqlite/index.htm




    'Knowledge' 카테고리의 다른 글

    Task And Thread In C#  (0) 2018.04.18
    [.NET] 텍스트 파일에서 읽기  (0) 2018.04.15
    [SQLite3] DataType  (0) 2018.04.15
    [WUP] SQLite 관련 Tutorial  (0) 2018.04.14
    [MS-SQL] 변수 및 테이블 변수 선언  (0) 2018.03.27

    1. Datatypes In SQLite

    Most SQL database engines (every SQL database engine other than SQLite, as far as we know) uses static, rigid typing. With static typing, the datatype of a value is determined by its container - the particular column in which the value is stored.

    SQLite uses a more general dynamic type system. In SQLite, the datatype of a value is associated with the value itself, not with its container. The dynamic type system of SQLite is backwards compatible with the more common static type systems of other database engines in the sense that SQL statements that work on statically typed databases should work the same way in SQLite. However, the dynamic typing in SQLite allows it to do things which are not possible in traditional rigidly typed databases.

    2. Storage Classes and Datatypes

    Each value stored in an SQLite database (or manipulated by the database engine) has one of the following storage classes:

    • NULL. The value is a NULL value.

    • INTEGER. The value is a signed integer, stored in 1, 2, 3, 4, 6, or 8 bytes depending on the magnitude of the value.

    • REAL. The value is a floating point value, stored as an 8-byte IEEE floating point number.

    • TEXT. The value is a text string, stored using the database encoding (UTF-8, UTF-16BE or UTF-16LE).

    • BLOB. The value is a blob of data, stored exactly as it was input.

    A storage class is more general than a datatype. The INTEGER storage class, for example, includes 6 different integer datatypes of different lengths. This makes a difference on disk. But as soon as INTEGER values are read off of disk and into memory for processing, they are converted to the most general datatype (8-byte signed integer). And so for the most part, "storage class" is indistinguishable from "datatype" and the two terms can be used interchangeably.

    Any column in an SQLite version 3 database, except an INTEGER PRIMARY KEY column, may be used to store a value of any storage class.

    All values in SQL statements, whether they are literals embedded in SQL statement text or parameters bound to precompiled SQL statements have an implicit storage class. Under circumstances described below, the database engine may convert values between numeric storage classes (INTEGER and REAL) and TEXT during query execution.

    2.1. Boolean Datatype

    SQLite does not have a separate Boolean storage class. Instead, Boolean values are stored as integers 0 (false) and 1 (true).

    2.2. Date and Time Datatype

    SQLite does not have a storage class set aside for storing dates and/or times. Instead, the built-in Date And Time Functions of SQLite are capable of storing dates and times as TEXT, REAL, or INTEGER values:

    • TEXT as ISO8601 strings ("YYYY-MM-DD HH:MM:SS.SSS").
    • REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
    • INTEGER as Unix Time, the number of seconds since 1970-01-01 00:00:00 UTC.

    Applications can chose to store dates and times in any of these formats and freely convert between formats using the built-in date and time functions.

    3. Type Affinity

    SQL database engines that use rigid typing will usually try to automatically convert values to the appropriate datatype. Consider this:

    CREATE TABLE t1(a INT, b VARCHAR(10));
    INSERT INTO t1(a,b) VALUES('123',456);
    

    Rigidly-typed database will convert the string '123' into an integer 123 and the integer 456 into a string '456' prior to doing the insert.

    In order to maximize compatibility between SQLite and other database engines, and so that the example above will work on SQLite as it does on other SQL database engines, SQLite supports the concept of "type affinity" on columns. The type affinity of a column is the recommended type for data stored in that column. The important idea here is that the type is recommended, not required. Any column can still store any type of data. It is just that some columns, given the choice, will prefer to use one storage class over another. The preferred storage class for a column is called its "affinity".

    Each column in an SQLite 3 database is assigned one of the following type affinities:

    • TEXT
    • NUMERIC
    • INTEGER
    • REAL
    • BLOB

    (Historical note: The "BLOB" type affinity used to be called "NONE". But that term was easy to confuse with "no affinity" and so it was renamed.)

    A column with TEXT affinity stores all data using storage classes NULL, TEXT or BLOB. If numerical data is inserted into a column with TEXT affinity it is converted into text form before being stored.

    A column with NUMERIC affinity may contain values using all five storage classes. When text data is inserted into a NUMERIC column, the storage class of the text is converted to INTEGER or REAL (in order of preference) if such conversion is lossless and reversible. For conversions between TEXT and REAL storage classes, SQLite considers the conversion to be lossless and reversible if the first 15 significant decimal digits of the number are preserved. If the lossless conversion of TEXT to INTEGER or REAL is not possible then the value is stored using the TEXT storage class. No attempt is made to convert NULL or BLOB values.

    A string might look like a floating-point literal with a decimal point and/or exponent notation but as long as the value can be expressed as an integer, the NUMERIC affinity will convert it into an integer. Hence, the string '3.0e+5' is stored in a column with NUMERIC affinity as the integer 300000, not as the floating point value 300000.0.

    A column that uses INTEGER affinity behaves the same as a column with NUMERIC affinity. The difference between INTEGER and NUMERIC affinity is only evident in a CAST expression.

    A column with REAL affinity behaves like a column with NUMERIC affinity except that it forces integer values into floating point representation. (As an internal optimization, small floating point values with no fractional component and stored in columns with REAL affinity are written to disk as integers in order to take up less space and are automatically converted back into floating point as the value is read out. This optimization is completely invisible at the SQL level and can only be detected by examining the raw bits of the database file.)

    A column with affinity BLOB does not prefer one storage class over another and no attempt is made to coerce data from one storage class into another.

    3.1. Determination Of Column Affinity

    The affinity of a column is determined by the declared type of the column, according to the following rules in the order shown:

    1. If the declared type contains the string "INT" then it is assigned INTEGER affinity.

    2. If the declared type of the column contains any of the strings "CHAR", "CLOB", or "TEXT" then that column has TEXT affinity. Notice that the type VARCHAR contains the string "CHAR" and is thus assigned TEXT affinity.

    3. If the declared type for a column contains the string "BLOB" or if no type is specified then the column has affinity BLOB.

    4. If the declared type for a column contains any of the strings "REAL", "FLOA", or "DOUB" then the column has REAL affinity.

    5. Otherwise, the affinity is NUMERIC.

    Note that the order of the rules for determining column affinity is important. A column whose declared type is "CHARINT" will match both rules 1 and 2 but the first rule takes precedence and so the column affinity will be INTEGER.

    3.1.1. Affinity Name Examples

    The following table shows how many common datatype names from more traditional SQL implementations are converted into affinities by the five rules of the previous section. This table shows only a small subset of the datatype names that SQLite will accept. Note that numeric arguments in parentheses that following the type name (ex: "VARCHAR(255)") are ignored by SQLite - SQLite does not impose any length restrictions (other than the large global SQLITE_MAX_LENGTH limit) on the length of strings, BLOBs or numeric values.

    Example Typenames From The
    CREATE TABLE Statement
    or CAST Expression
    Resulting AffinityRule Used To Determine Affinity
    INT
    INTEGER
    TINYINT
    SMALLINT
    MEDIUMINT
    BIGINT
    UNSIGNED BIG INT
    INT2
    INT8
    INTEGER1
    CHARACTER(20)
    VARCHAR(255)
    VARYING CHARACTER(255)
    NCHAR(55)
    NATIVE CHARACTER(70)
    NVARCHAR(100)
    TEXT
    CLOB
    TEXT2
    BLOB
    no datatype specified
    BLOB3
    REAL
    DOUBLE
    DOUBLE PRECISION
    FLOAT
    REAL4
    NUMERIC
    DECIMAL(10,5)
    BOOLEAN
    DATE
    DATETIME
    NUMERIC5

    Note that a declared type of "FLOATING POINT" would give INTEGER affinity, not REAL affinity, due to the "INT" at the end of "POINT". And the declared type of "STRING" has an affinity of NUMERIC, not TEXT.

    3.2. Affinity Of Expressions

    Every table column has a type affinity (one of BLOB, TEXT, INTEGER, REAL, or NUMERIC) but expressions do no necessarily have an affinity.

    Expression affinity is determined by the following rules:

    • The right-hand operand of an IN or NOT IN operator has no affinity if the operand is a list and has the same affinity as the affinity of the result set expression if the operand is a SELECT.

    • When an expression is a simple reference to a column of a real table (not a VIEW or subquery) then the expression has the same affinity as the table column.

      • Parentheses around the column name are ignored. Hence if X and Y.Z are column names, then (X) and (Y.Z) are also considered column names and have the affinity of the corresponding columns.

      • Any operators applied to column names, including the no-op unary "+" operator, convert the column name into an expression which always has no affinity. Hence even if X and Y.Z are column names, the expressions +X and +Y.Z are not column names and have no affinity.

    • An expression of the form "CAST(expr AS type)" has an affinity that is the same as a column with a declared type of "type".

    • Otherwise, an expression has no affinity.

    3.3. Column Affinity For Views And Subqueries

    The "columns" of a VIEW or FROM-clause subquery are really the expressions in the result set of the SELECT statement that implements the VIEW or subquery. Thus, the affinity for columns of a VIEW or subquery are determined by the expression affinity rules above. Consider an example:

    CREATE TABLE t1(a INT, b TEXT, c REAL);
    CREATE VIEW v1(x,y,z) AS SELECT b, a+c, 42 FROM t1 WHERE b!=11;
    

    The affinity of the v1.x column will be the same as the affinity of t1.b (INTEGER), since v1.x maps directly into t1.b. But columns v1.y and v1.z both have no affinity, since those columns map into expression a+c and 42, and expressions always have no affinity.

    When the SELECT statement that implements a VIEW or FROM-clause subquery is a compound SELECT then the affinity of each supposed column of the VIEW or subquery will be the affinity of the corresponding result column for one of the individual SELECT statements that make up the compound. However, it is indeterminate which of the SELECT statements will be used to determine affinity. Different constituent SELECT statements might be used to determine affinity at different times during query evaluation. Best practice is to avoid mixing affinities in a compound SELECT.

    3.4. Column Affinity Behavior Example

    The following SQL demonstrates how SQLite uses column affinity to do type conversions when values are inserted into a table.

    CREATE TABLE t1(
        t  TEXT,     -- text affinity by rule 2
        nu NUMERIC,  -- numeric affinity by rule 5
        i  INTEGER,  -- integer affinity by rule 1
        r  REAL,     -- real affinity by rule 4
        no BLOB      -- no affinity by rule 3
    );
    
    -- Values stored as TEXT, INTEGER, INTEGER, REAL, TEXT.
    INSERT INTO t1 VALUES('500.0', '500.0', '500.0', '500.0', '500.0');
    SELECT typeof(t), typeof(nu), typeof(i), typeof(r), typeof(no) FROM t1;
    text|integer|integer|real|text
    
    -- Values stored as TEXT, INTEGER, INTEGER, REAL, REAL.
    DELETE FROM t1;
    INSERT INTO t1 VALUES(500.0, 500.0, 500.0, 500.0, 500.0);
    SELECT typeof(t), typeof(nu), typeof(i), typeof(r), typeof(no) FROM t1;
    text|integer|integer|real|real
    
    -- Values stored as TEXT, INTEGER, INTEGER, REAL, INTEGER.
    DELETE FROM t1;
    INSERT INTO t1 VALUES(500, 500, 500, 500, 500);
    SELECT typeof(t), typeof(nu), typeof(i), typeof(r), typeof(no) FROM t1;
    text|integer|integer|real|integer
    
    -- BLOBs are always stored as BLOBs regardless of column affinity.
    DELETE FROM t1;
    INSERT INTO t1 VALUES(x'0500', x'0500', x'0500', x'0500', x'0500');
    SELECT typeof(t), typeof(nu), typeof(i), typeof(r), typeof(no) FROM t1;
    blob|blob|blob|blob|blob
    
    -- NULLs are also unaffected by affinity
    DELETE FROM t1;
    INSERT INTO t1 VALUES(NULL,NULL,NULL,NULL,NULL);
    SELECT typeof(t), typeof(nu), typeof(i), typeof(r), typeof(no) FROM t1;
    null|null|null|null|null
    

    4. Comparison Expressions

    SQLite version 3 has the usual set of SQL comparison operators including "=", "==", "<", "<=", ">", ">=", "!=", "", "IN", "NOT IN", "BETWEEN", "IS", and "IS NOT", .

    4.1. Sort Order

    The results of a comparison depend on the storage classes of the operands, according to the following rules:

    • A value with storage class NULL is considered less than any other value (including another value with storage class NULL).

    • An INTEGER or REAL value is less than any TEXT or BLOB value. When an INTEGER or REAL is compared to another INTEGER or REAL, a numerical comparison is performed.

    • A TEXT value is less than a BLOB value. When two TEXT values are compared an appropriate collating sequence is used to determine the result.

    • When two BLOB values are compared, the result is determined using memcmp().

    4.2. Type Conversions Prior To Comparison

    SQLite may attempt to convert values between the storage classes INTEGER, REAL, and/or TEXT before performing a comparison. Whether or not any conversions are attempted before the comparison takes place depends on the type affinity of the operands.

    To "apply affinity" means to convert an operand to a particular storage class if and only if the conversion is lossless and reversible. Affinity is applied to operands of a comparison operator prior to the comparison according to the following rules in the order shown:

    • If one operand has INTEGER, REAL or NUMERIC affinity and the other operand has TEXT or BLOB or no affinity then NUMERIC affinity is applied to other operand.

    • If one operand has TEXT affinity and the other has no affinity, then TEXT affinity is applied to the other operand.

    • Otherwise, no affinity is applied and both operands are compared as is.

    The expression "a BETWEEN b AND c" is treated as two separate binary comparisons "a >= b AND a <= c", even if that means different affinities are applied to 'a' in each of the comparisons. Datatype conversions in comparisons of the form "x IN (SELECT y ...)" are handled is if the comparison were really "x=y". The expression "a IN (x, y, z, ...)" is equivalent to "a = +x OR a = +y OR a = +z OR ...". In other words, the values to the right of the IN operator (the "x", "y", and "z" values in this example) are considered to have no affinity, even if they happen to be column values or CAST expressions.

    4.3. Comparison Example

    CREATE TABLE t1(
        a TEXT,      -- text affinity
        b NUMERIC,   -- numeric affinity
        c BLOB,      -- no affinity
        d            -- no affinity
    );
    
    -- Values will be stored as TEXT, INTEGER, TEXT, and INTEGER respectively
    INSERT INTO t1 VALUES('500', '500', '500', 500);
    SELECT typeof(a), typeof(b), typeof(c), typeof(d) FROM t1;
    text|integer|text|integer
    
    -- Because column "a" has text affinity, numeric values on the
    -- right-hand side of the comparisons are converted to text before
    -- the comparison occurs.
    SELECT a < 40,   a < 60,   a < 600 FROM t1;
    0|1|1
    
    -- Text affinity is applied to the right-hand operands but since
    -- they are already TEXT this is a no-op; no conversions occur.
    SELECT a < '40', a < '60', a < '600' FROM t1;
    0|1|1
    
    -- Column "b" has numeric affinity and so numeric affinity is applied
    -- to the operands on the right.  Since the operands are already numeric,
    -- the application of affinity is a no-op; no conversions occur.  All
    -- values are compared numerically.
    SELECT b < 40,   b < 60,   b < 600 FROM t1;
    0|0|1
    
    -- Numeric affinity is applied to operands on the right, converting them
    -- from text to integers.  Then a numeric comparison occurs.
    SELECT b < '40', b < '60', b < '600' FROM t1;
    0|0|1
    
    -- No affinity conversions occur.  Right-hand side values all have
    -- storage class INTEGER which are always less than the TEXT values
    -- on the left.
    SELECT c < 40,   c < 60,   c < 600 FROM t1;
    0|0|0
    
    -- No affinity conversions occur.  Values are compared as TEXT.
    SELECT c < '40', c < '60', c < '600' FROM t1;
    0|1|1
    
    -- No affinity conversions occur.  Right-hand side values all have
    -- storage class INTEGER which compare numerically with the INTEGER
    -- values on the left.
    SELECT d < 40,   d < 60,   d < 600 FROM t1;
    0|0|1
    
    -- No affinity conversions occur.  INTEGER values on the left are
    -- always less than TEXT values on the right.
    SELECT d < '40', d < '60', d < '600' FROM t1;
    1|1|1
    

    All of the result in the example are the same if the comparisons are commuted - if expressions of the form "a<40" are rewritten as "40>a".

    5. Operators

    All mathematical operators (+, -, *, /, %, <<, >>, &, and |) cast both operands to the NUMERIC storage class prior to being carried out. The cast is carried through even if it is lossy and irreversible. A NULL operand on a mathematical operator yields a NULL result. An operand on a mathematical operator that does not look in any way numeric and is not NULL is converted to 0 or 0.0.

    6. Sorting, Grouping and Compound SELECTs

    When query results are sorted by an ORDER BY clause, values with storage class NULL come first, followed by INTEGER and REAL values interspersed in numeric order, followed by TEXT values in collating sequence order, and finally BLOB values in memcmp() order. No storage class conversions occur before the sort.

    When grouping values with the GROUP BY clause values with different storage classes are considered distinct, except for INTEGER and REAL values which are considered equal if they are numerically equal. No affinities are applied to any values as the result of a GROUP by clause.

    The compound SELECT operators UNION, INTERSECT and EXCEPT perform implicit comparisons between values. No affinity is applied to comparison operands for the implicit comparisons associated with UNION, INTERSECT, or EXCEPT - the values are compared as is.

    7. Collating Sequences

    When SQLite compares two strings, it uses a collating sequence or collating function (two words for the same thing) to determine which string is greater or if the two strings are equal. SQLite has three built-in collating functions: BINARY, NOCASE, and RTRIM.

    • BINARY - Compares string data using memcmp(), regardless of text encoding.
    • NOCASE - The same as binary, except the 26 upper case characters of ASCII are folded to their lower case equivalents before the comparison is performed. Note that only ASCII characters are case folded. SQLite does not attempt to do full UTF case folding due to the size of the tables required.
    • RTRIM - The same as binary, except that trailing space characters are ignored.

    An application can register additional collating functions using the sqlite3_create_collation() interface.

    7.1. Assigning Collating Sequences from SQL

    Every column of every table has an associated collating function. If no collating function is explicitly defined, then the collating function defaults to BINARY. The COLLATE clause of the column definition is used to define alternative collating functions for a column.

    The rules for determining which collating function to use for a binary comparison operator (=, <, >, <=, >=, !=, IS, and IS NOT) are as follows:

    1. If either operand has an explicit collating function assignment using the postfix COLLATE operator, then the explicit collating function is used for comparison, with precedence to the collating function of the left operand.

    2. If either operand is a column, then the collating function of that column is used with precedence to the left operand. For the purposes of the previous sentence, a column name preceded by one or more unary "+" operators is still considered a column name.

    3. Otherwise, the BINARY collating function is used for comparison.

    An operand of a comparison is considered to have an explicit collating function assignment (rule 1 above) if any subexpression of the operand uses the postfix COLLATE operator. Thus, if a COLLATE operator is used anywhere in a comparision expression, the collating function defined by that operator is used for string comparison regardless of what table columns might be a part of that expression. If two or more COLLATE operatorsubexpressions appear anywhere in a comparison, the left most explicit collating function is used regardless of how deeply the COLLATE operators are nested in the expression and regardless of how the expression is parenthesized.

    The expression "x BETWEEN y and z" is logically equivalent to two comparisons "x >= y AND x <= z" and works with respect to collating functions as if it were two separate comparisons. The expression "x IN (SELECT y ...)" is handled in the same way as the expression "x = y" for the purposes of determining the collating sequence. The collating sequence used for expressions of the form "x IN (y, z, ...)" is the collating sequence of x.

    Terms of the ORDER BY clause that is part of a SELECT statement may be assigned a collating sequence using the COLLATE operator, in which case the specified collating function is used for sorting. Otherwise, if the expression sorted by an ORDER BY clause is a column, then the collating sequence of the column is used to determine sort order. If the expression is not a column and has no COLLATE clause, then the BINARY collating sequence is used.

    7.2. Collation Sequence Examples

    The examples below identify the collating sequences that would be used to determine the results of text comparisons that may be performed by various SQL statements. Note that a text comparison may not be required, and no collating sequence used, in the case of numeric, blob or NULL values.

    CREATE TABLE t1(
        x INTEGER PRIMARY KEY,
        a,                 /* collating sequence BINARY */
        b COLLATE BINARY,  /* collating sequence BINARY */
        c COLLATE RTRIM,   /* collating sequence RTRIM  */
        d COLLATE NOCASE   /* collating sequence NOCASE */
    );
                       /* x   a     b     c       d */
    INSERT INTO t1 VALUES(1,'abc','abc', 'abc  ','abc');
    INSERT INTO t1 VALUES(2,'abc','abc', 'abc',  'ABC');
    INSERT INTO t1 VALUES(3,'abc','abc', 'abc ', 'Abc');
    INSERT INTO t1 VALUES(4,'abc','abc ','ABC',  'abc');
     
    /* Text comparison a=b is performed using the BINARY collating sequence. */
    SELECT x FROM t1 WHERE a = b ORDER BY x;
    --result 1 2 3
    
    /* Text comparison a=b is performed using the RTRIM collating sequence. */
    SELECT x FROM t1 WHERE a = b COLLATE RTRIM ORDER BY x;
    --result 1 2 3 4
    
    /* Text comparison d=a is performed using the NOCASE collating sequence. */
    SELECT x FROM t1 WHERE d = a ORDER BY x;
    --result 1 2 3 4
    
    /* Text comparison a=d is performed using the BINARY collating sequence. */
    SELECT x FROM t1 WHERE a = d ORDER BY x;
    --result 1 4
    
    /* Text comparison 'abc'=c is performed using the RTRIM collating sequence. */
    SELECT x FROM t1 WHERE 'abc' = c ORDER BY x;
    --result 1 2 3
    
    /* Text comparison c='abc' is performed using the RTRIM collating sequence. */
    SELECT x FROM t1 WHERE c = 'abc' ORDER BY x;
    --result 1 2 3
    
    /* Grouping is performed using the NOCASE collating sequence (Values
    ** 'abc', 'ABC', and 'Abc' are placed in the same group). */
    SELECT count(*) FROM t1 GROUP BY d ORDER BY 1;
    --result 4
    
    /* Grouping is performed using the BINARY collating sequence.  'abc' and
    ** 'ABC' and 'Abc' form different groups */
    SELECT count(*) FROM t1 GROUP BY (d || '') ORDER BY 1;
    --result 1 1 2
    
    /* Sorting or column c is performed using the RTRIM collating sequence. */
    SELECT x FROM t1 ORDER BY c, x;
    --result 4 1 2 3
    
    /* Sorting of (c||'') is performed using the BINARY collating sequence. */
    SELECT x FROM t1 ORDER BY (c||''), x;
    --result 4 2 3 1
    
    /* Sorting of column c is performed using the NOCASE collating sequence. */
    SELECT x FROM t1 ORDER BY c COLLATE NOCASE, x;
    --result 2 4 3 1


    'Knowledge' 카테고리의 다른 글

    [.NET] 텍스트 파일에서 읽기  (0) 2018.04.15
    [SQLite3] 튜토리얼 사이트  (0) 2018.04.15
    [WUP] SQLite 관련 Tutorial  (0) 2018.04.14
    [MS-SQL] 변수 및 테이블 변수 선언  (0) 2018.03.27
    [MS-SQL] 사용자 추가  (0) 2018.03.23

    메소드 콜에서 UI를 조작하면 다중 스레드에서 에러 발생합니다. 주의해주세요. 

    Control.Invoke 에서 행업 걸립니다...

    BeginInvoke에서는 괜찮지만, EndInvoke 에서 행업걸립니다...

    다중 스레드에서 컨트롤러 표기에 관한 부분은 메소드 콜에서 자제해주세요.



    MainPage 클레스에 정적 LogMethod 메소드를 만들어 주세요. 

    Rule에 의해 움직이기 때문에, 해당 클레스 뿐 아니라 다른 곳에서 찎은 로그도 이곳에 로그가 발생합니다.


    // NLog 이벤트 등록 메소드

    private void Register_NLog_MethodCall()

    {

    MethodCallTarget target = new MethodCallTarget();

    target.ClassName = typeof(MainPage).AssemblyQualifiedName;

    target.MethodName = "LogMethod";

    target.Parameters.Add(new MethodCallParameter("${level}"));

    target.Parameters.Add(new MethodCallParameter("${message}"));


    var wrapper = new AsyncTargetWrapper(target, 5000, AsyncTargetWrapperOverflowAction.Discard);

    var rule = new LoggingRule("*", LogLevel.Trace, target);


    NLog.LogManager.Configuration.AddTarget("method", wrapper);

    NLog.LogManager.Configuration.LoggingRules.Add(rule);

    NLog.LogManager.ReconfigExistingLoggers();


    // 리스트 박스 등록

    _lbx = lbx_log;

    }

    'Knowledge > NLog' 카테고리의 다른 글

    NLog 설정 Programmically 등록 (수동)  (0) 2018.04.26

    + Recent posts