- LINQ의 기본
- from
- where
- order By
- select
- 여러개의 데이터 원본에 질의하기
- group by로 데이터 분류하기
- 내부 조인
- 외부 조인
- LINQ의 비밀과 LINQ 표준 연산자
[ 데이터! ]
[ LINQ ]
- LINQ는 Language INtegrated Query (통합 언어 퀴리)로 C#의 통합된 데이터 질의 기능을 말한다 .
- 데이터 질의는 데이터에 대해 물어본다는 말 . 기본적으로 질의는 다음 내용을 포함한다 .
- From : 어떤 데이터 집합에서 찾을 것인가?
- Where : 어떤 값의 데이터를 찾을 것인가?
- Select : 어떤 항목을 추출할 것인가?
[ 예시 ]
class Profile
{
public string Name{get;set;}
public int Height{get;set;}
}
Profile[] arrProfile =
{
new Profile(){Name="정우성",Height=186},
new Profile(){Name="김태희",Height=158},
new Profile(){Name="고현정",Height=156}
};
- arrProfile에서 Height 프로퍼티가 175 미만인 데이터만 골라 새 컬렉션으로 추가해보자
List<Profile>profiles = new List<Profile>();
foeach(Profile profile in arrProfile)
{
if(profile.Height < 175)
profiles.Add(profile);
}
profile.Sort(
(profile1,profile2)=>
{
return profile1.Height - profile2.Height;
});
- 다음과 같이 정렬 할 수도 있다 .
var profiles = from profile in arrProfile
where profile.Height < 175
orderby profile.Height
select profile;
- LINQ를 사용하면 위와 같이 단순하게 만들 수 있다.
[ From ]
[ from ]
- 모든 LINQ 퀴리식은 반드시 from 절로 시작한다 .
- 퀴리식의 대상이 될 데이터 원본과 데이터 원본 안에 들어 있는 각 요소 데이터를 나타내는 범위변수를 지정해야 한다 .
- from 데이터 원본은 IEnumerable<T> 인터페이스를 상속하는 형식이어야만 한다 .
- 범위 변수는 퀴리 변수라고도 한다 .
[ LINQ의 범위 변수와 foreach문의 반복변수의 차이점 ]
- foreach 문의 반복 변수는 데이터 원본으로부터 데이터를 담지만 범위변수는 실제로 데이터를 담지는 않는다
- 퀴리식 외부에서 선언된 변수에 범위 변수의 데이터를 복사하는 행위는 할 수 없다 .
- 범위 변수는 오직 LINQ 질의 안에서만 통용된다 .
[ from 절의 사용 ]
var result = from 범위변수 in 데이터 원본
- from 절은 다음과 같이 from <범위변수> in <데이터 원본>의 형식으로 사용한다 .
- 데이터 원본으로부터 범위변수를 뽑은 후 LINQ 가 제공하는 수십가지 연산자로 데이터를 가공/추출 한다 .
using System;
using System.Linq;
namespace ConsoleApp;
class MainApp
{
static void Main(string[] args)
{
int[] numbers = { 9, 2, 6, 4, 5, 3, 7, 8, 1, 10 };
var result =from number in numbers
where number%2==0
orderby number
select number;
foreach (var number in result)
{
Console.WriteLine($"짝수 : {number}");
}
}
}
[ Where ]
[ where ]
- 필터 역할을 하는 연산자이다 .
- from 절이 데이터 원본으로부터 뽑아낸 범위변수가 가져야하는 조건을 where 연산자에 인수로 입력하면
LINQ는 해당 조건에 부합하는 데이터만을 걸러낸다 .
var profiles = from profile in arrProfile
where profile < 175
[ orderby ]
[ orderby ]
- 데이터 정렬을 수행하는 연산자이다 .
- orderby 연산자는 기본적으로 오름차순으로 데이터를 정렬하지만 , 키워드로 명시적으로 표현할 수 있다 .
- ascending은 오름차순을 descending은 내림차순으로 정렬한다 .
var profiles = from profile in arrProfile
where profile.heignt < 175
orderby profile.height ascending
orderby profile.height descending
[ select ]
[ select ]
var profiles = from profile in arrProfiles
where profile %2==0
orderby profile.height
select profile
//select profile.Name
- select 절은 최종 결과를 추출하는 퀴리식의 마침표 같은 존재이다 .
- LINQ 질의의 결과는 IEnumerable<T>로 반환되는데 , 이때 형식 매개변수 T는 select 문에 의해 결정된다 .
- profile select시 IEnumerable<Profile> 형식이 , profile.Name 선택시 IEnumerable<string>형식으로 컴파일된다 .
select new {Name = profile.Name , InchHeight =profile.Height * 0.393};
- 또한 무명형식을 이용해 새로운 형식을 즉석에서 만들수도 있다 .
using System;
using System.Linq;
namespace ConsoleApp;
class Profile
{
public string Name { get; set; }
public int Height { get; set; }
}
class MainApp
{
static void Main(string[] args)
{
Profile[] arrProfile =
{
new Profile(){Name="정우성",Height=186 },
new Profile(){Name="김태희",Height=158 },
new Profile(){Name="고현정",Height=172 },
new Profile(){Name="이문세",Height=178 },
new Profile(){Name="하하",Height=171 },
};
var profiles = from profile in arrProfile
where profile.Height < 175
orderby profile.Height
select new
{
Name = profile.Name,
InchHeight = profile.Height * 0.393
};
foreach (var profile in profiles)
Console.WriteLine($"{profile.Name},{profile.InchHeight}");
}
}
[ 여러 개의 데이터 원본에 질의하기 ]
[ select ]
using System;
using System.Linq;
namespace ConsoleApp;
class Class
{
public string Name { get; set; }
public int [] Score { get; set; }
}
class MainApp
{
static void Main(string[] args)
{
Class[] arrClass =
{
new Class(){ Name="연두반",Score=new int[]{ 99,80,70,24} },
new Class(){ Name="분홍반",Score=new int[]{ 60,45,87,72} },
new Class(){ Name="파랑반",Score=new int[]{ 92,30,85,94} },
new Class(){ Name="노랑반",Score=new int[]{ 90,88,0,17} }
};
var classes = from c in arrClass
from s in c.Score
where s < 60
orderby s
select new { c.Name,Lowest = s};
foreach(var c in classes)
Console.WriteLine($"낙제 : {c.Name},({c.Lowest})");
}
}
- 여러 개의 데이터 원본에 접근하려면 from 문을 중첩해서 사용하면 된다 .
- from 절의 중첩으로 낙제점을 맞은 학생의 학급과 점수를 담아낸다.
[ groupby로 데이터 분류하기 ]
[ groupby ]
group 범위변수 by 기준 into 그룹 변수
- groupby절을 통해 분류기준에 따라 데이터를 그룹화 할 수 있다 .
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace ConsoleApp;
class MainApp
{
class Profile
{
public string Name { get; set; }
public int Height { get; set; }
}
static void Main(string[] args)
{
Profile[] arrProfile =
{
new Profile(){Name="정우성",Height=186 },
new Profile(){Name="김태희",Height=158 },
new Profile(){Name="고현정",Height=172 },
new Profile(){Name="이문세",Height=178 },
new Profile(){Name="하하",Height=171 },
};
var listProfile = from profile in arrProfile
orderby profile.Height
group profile by profile.Height < 175 into g
//listProfile에는 조건(175 이하)에 따라 두 그룹이 형성
select new {GroupKey=g.Key,Profiles=g};
foreach(var group in listProfile)
{
Console.WriteLine($" - 175 미만 ? : {group.GroupKey}");
foreach(var profile in group.Profiles)
Console.WriteLine($">>> {profile.Name} , {profile.Height}");
}
}
}
[ 두 데이터 원본을 연결하는 join ]
[ join ]
- 각 데이터 원본에서 특정 필드의 값을 비교하여 일치하는 데이터끼리 연결한다 .
[ 내부 join ]
- 교집합과 같다 .
- 내부 조인은 첫번째 데이터 원본과 두번째 데이터 원본의 특정 필드를 비교 , 일치하는 데이터를 반환한다 .
- 이때의 기준은 첫 번째 원본 데이터이다 .
- 내부 조인을 수행시 기준 데이터 원본에는 있지만 연결할 데이터 원본에는 없다면 조인 결과에 포함 안된다 .
- join 절의 on 키워드는 조인 조건을 수반하는데 이때 , 비교가 아닌 동등의 키워드 equlas만 가능하다 .
from a in A
join b in B on a.xxx equlas b.yyy
[ 외부 join ]
- 조인 기준이 되는 데이터의 원본이 모두 포함된다 .
- 외부 조인의 기준이 되는 데이터 원본의 모든 데이터를 조인결과에 반드시 포함시킨다
- 일치값이 없다면 빈 값으로 결과를 채우게 된다
- join 절을 이용해서 조인을 수행 , 해당 결과를 임시 컬렉션에 저장한다 .
- 임시 컬렉션에 DefaultIfEmpty연산을 수행 , 빈 조인결과에 빈 값을 채워넣는다 .
from a in A
join b in B on a.xxx equlas b.yyy into c
from b in c.DefaultIfEmpty(new B(){빈값})
[ join예시 ]
using System;
using System.Linq;
using System.Text.RegularExpressions;
namespace ConsoleApp;
class MainApp
{
class Profile
{
public string Name { get; set; }
public int Height { get; set; }
}
class Product
{
public string Title { get; set; }
public string Star { get; set; }
}
static void Main(string[] args)
{
Profile[] arrProfile =
{
new Profile(){Name="정우성",Height=186 },
new Profile(){Name="김태희",Height=158 },
new Profile(){Name="고현정",Height=172 },
new Profile(){Name="이문세",Height=178 },
new Profile(){Name="하하",Height=171 },
};
Product[] arrProduct =
{
new Product(){ Title="비트",Star="정우성"},
new Product(){ Title="CF 다수",Star="김태희"},
new Product(){ Title="아이리스",Star="김태희"},
new Product(){ Title="모래시계",Star="고현정"},
new Product(){ Title="Solo 예찬",Star="이문세"},
};
var listProfile =
from profile in arrProfile
join product in arrProduct on profile.Name equals product.Star
select new
{
Name = profile.Name,
Work = product.Title,
Height = profile.Height
};
Console.WriteLine("--- 내부 조인 결과 ---");
foreach(var profile in listProfile)
{
Console.WriteLine($"이름 : {profile.Name} 키 : {profile.Height} 작품 : {profile.Work}");
}
listProfile = from profile in arrProfile
join product in arrProduct on profile.Name equals product.Star into ps
from product in ps.DefaultIfEmpty(new Product() { Title = "그런거 없음" })
select new
{
Name = profile.Name,
Work=product.Title,
Height=profile.Height,
};
Console.WriteLine("--- 외부 조인 결과 ---");
foreach (var profile in listProfile)
{
Console.WriteLine($"이름 : {profile.Name} 키 : {profile.Height} 작품 : {profile.Work}");
}
}
}
[ LINQ의 비밀과 LINQ 표준 연산자 ]
[ LINQ의 비밀 ]
- LINQ는 다른 .NET 언어에서는 사용 할 수 없다 (C# , VB만 가능)
- 마이크로소프트는 LINQ 퀴리식이 실행될 수 있게 CLR을 개선하는 대신 , C#/VB 컴파일러를 업그레이드 했다 .
- 컴파일러가 LINQ 퀴리식을 CLR이 이해할 수 있는 코드로 번역되는 업그레이드를 하였다 .
- 컴파일러는 LINQ 퀴리식을 분석 , 일반적인 메소드 호출 코드로 만들어냈다. 예시는 아래와 같다 .
//퀴리식
var profiles = from profile in arrProfile
where profile.Height < 175
orderby profile.Height
select new {Name=profile.Name,InchHeight=profile.Height*0.393 };
//C# 컴파일러가 번역
var profiles = arrProfile
.Where(profile => profile.Height < 175)
.OrderBy(profiles => profiles.Height)
.Select(profiles =>
new
{
Name = profile.Name,
InchHeight = profile.Height * 0.393
});
- where은 Where()메소드 / orderBY는 OrderBy() / select는 Select() /from절의 범위변수는
각 메소드에 입력되는 람다식의 매개변수로 바뀌었다.
- from 절의 매개변수는 IEnumerable<T>의 파생형식이어야 한다 .
- 위 예제의 배열은 IEnumerable<T>의 파생형식이며 , System.Collections.Generic의 소속이다 .
- Where(),OrderBy(),Select() 메서드는 System.Linq에 정의되어 있는 IEnumerable의 확장메소드이다 .
using System;
using System.ComponentModel.Design;
using System.Linq;
using System.Text.RegularExpressions;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace ConsoleApp;
class Profile
{
public int Height { get; set; }
public string Name { get; set; }
}
class MainApp
{
static void Main(string[] args)
{
Profile[] arrProfile =
{
new Profile(){Name="정우성",Height=186 },
new Profile(){Name="김태희",Height=158 },
new Profile(){Name="고현정",Height=172 },
new Profile(){Name="이문세",Height=178 },
new Profile(){Name="하하",Height=171 },
};
var profiles = arrProfile
.Where(profile => profile.Height < 175)
.OrderBy(profile => profile.Height)
.Select(profile =>
new
{
Name = profile.Name,
InchHeight = profile.Height * 0.393
});
foreach(var profile in profiles)
Console.WriteLine($"{profile.Name} , {profile.InchHeight}");
}
}
[ LINQ 표준 연산 메소드와 C#의 퀴리식 ]
- 53개의 표준 LINQ 연산 메소드 중 C#의 퀴리식에서 지원하는 것은 11개 뿐이다.
- LINQ 퀴리식과 메소드를 함께 사용한다면 더욱 편할 것이다 .
using System;
using System.Linq;
namespace ConsoleApp;
class Profile
{
public int Height { get; set; }
public string Name { get; set; }
}
class MainApp
{
static void Main(string[] args)
{
Profile[] arrProfile =
{
new Profile(){Name="정우성",Height=186 },
new Profile(){Name="김태희",Height=158 },
new Profile(){Name="고현정",Height=172 },
new Profile(){Name="이문세",Height=178 },
new Profile(){Name="하하",Height=171 },
};
var heightStat = from profile in arrProfile
group profile by profile.Height < 175 into g
select new
{
Group = g.Key == true ? "175미만" : "이상",
Count=g.Count(),
Max=g.Max(profile=>profile.Height),
Min=g.Min(profile=>profile.Height),
Average=g.Average(profile=>profile.Height),
};
foreach(var stat in heightStat)
{
Console.Write("{0} - Count : {1},Max:{2}",stat.Group,stat.Count,stat.Max);
Console.WriteLine("Min : {0} Average : {1}", stat.Min, stat.Average);
}
}
}
'C# > 이것이 C#이다' 카테고리의 다른 글
[ 이것이 C#이다 ] Chapter 18. 파일 다루기 (0) | 2024.04.11 |
---|---|
[ 이것이 C#이다 ] Chapter 16 . 리플렉션과 애트리뷰트 (0) | 2024.04.02 |
[ 이것이 C#이다 ] Chapter 14 . 람다식 (0) | 2024.03.25 |
[ 이것이 C#이다 ] Chapter 13 . 대리자와 이벤트 (0) | 2024.03.21 |
[ 이것이 C#이다 ] Chapter 12 . 예외처리 (0) | 2024.03.20 |