블로그 이미지
따시쿵

calendar

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

Notice

2015. 6. 25. 14:11 C# with LINQ to SQL

이번 글에서는 LINQ와 실제 데이타베이스(MS SQL 2012)를 연결해서 데이타를 조회하는 화면을 만들어 보겠습니다.

 

시나리오는 데이타베이스에 있는 테이블에서 사용자 아이디와 비밀번호를 비교해서 같은지 다른지를 풀력하는 프로그램 입니다.

 

먼저, 결과 화면부터 보겠습니다. 성공과 실패 두가지 입니다.

 

 

 

테이블 명세서와 테이타입니다.

 

 

 

우리는 프로그램을 공부하는 사람들이니 아래 박스의 순서에 따라 작업 해 주시면 됩니다.

 

1.  프로젝트 생성하고 dbml 파일 생성하기

 

Visual Studio 2013 을 실행 시킨 다음에, 프로젝트를 Windows form 타입으로 생성을 합니다.

프로젝트는 생성 한 후, [추가] => [새항목] =>[ 데이타] =>[LINQ to SQL 클래스] 선택하고 login.dbml 파일을 만듭니다. 파일을 만들고 나면, 왼쪽 오른쪽 분할 윈도우가 나오는데 왼쪽에는 테이블을 끌어다 놓고, 오른쪽에는 사용할 스토어프로시져를 끌어다 놓습니다.

 

login.dbml 파일을 만들었으면, DataContext() 클래스에서 상속받아서 loginDataContext() 을 자동적으로 만들어 줍니다. 이 파일은 logn.designer.cs 파일을 열면 확인이 가능합니다. tbl_member class 도 같이 자동으로 만들어 줍니다.

 

loginDataContext 클래스를 이용해서 테이타 쿼리가 이루어집니다.

 

 

 

2. 로그인 버튼에 click 이벤트를 추가 합니다.

LINQ 쿼리를 사용해서 디비 테이블의 필드값을 조회하는 쿼리는 유심히 확인하시기 바랍니다.

        private void btnLogin_Click(object sender, EventArgs e)
        {
            loginDataContext myDB = new loginDataContext();

            var userResult = from u in myDB.tbl_member
                             where u.user_name == this.textUserID.Text
                             && u.user_password == this.textUserPassword.Text
                             select u;

            if (Enumerable.Count(userResult) > 0)
                MessageBox.Show("로그인 성공", "로그인 확인", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            else
                MessageBox.Show("로그인 실패", "로그인 확인", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

Enmerable.Count 는 쿼리가 실행되고 난 후, 결과 row 의 개수를 리턴해 줍니다.

 

 

3. SQL 문법 없이 사용하는 방법

2번에서 사용한 쿼리문을 다시 다음과 같이 IQueryable interface를 이용해서 다음과 같이 표현 할 수도 있습니다.

        private void btnLogin_Click(object sender, EventArgs e)
        {
            loginDataContext myDB = new loginDataContext();

            var userResult = myDB.tbl_member.Where(u => u.user_name == textUserID.Text 
                                                       && u.user_password == textUserPassword.Text);
            if (userResult.Count() > 0)
                MessageBox.Show("로그인 성공", "로그인 확인", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            else
                MessageBox.Show("로그인 실패", "로그인 확인", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

4. Single row 를 쿼리하기

로그인 화면에 [전화번호 쿼리하기] 버튼을 생성하고, 사용자 전화번호를 가져오는 퀴리를 만들어 봤습니다.

            loginDataContext myDB = new loginDataContext();

            try
            {
                tbl_member info = myDB.tbl_member.Single(u => u.user_name == textUserID.Text);
                MessageBox.Show(info.telephone);
            }
            catch(System.InvalidOperationException ex)
            {
                MessageBox.Show("사용자 입력이 잘못 되었습니다.");
            }

 

전제 소스

MyLINQ8.zip

 

posted by 따시쿵
2015. 6. 22. 11:43 C# with LINQ to SQL

Join 절에 대해서 알아 보도록 하겠습니다.

 

join 절은 개체 모델에서 직접 관계가 없는 여러 소스 시퀀스의 요소를 연결하는 데 유용합니다. 유일한 요구 사항은 각 소스의 요소가 같은지 비교할 수 있는 일부 값을 공유해야 한다는 것입니다. 예를 들어 식품 유통업체에는 특정 제품의 공급자 목록과 구매자 목록이 있을 수 있습니다. 예를 들어 해당 제품의 공급자와 구매자가 모두 동일한 특정 지역에 있는 경우 join 절을 사용하여 이러한 공급자와 구매자의 목록을 만들 수 있습니다.


join 절은 두 개의 소스 시퀀스를 입력으로 사용합니다. 각 시퀀스의 요소는 다른 시퀀스의 해당 속성과 비교할 수 있는 속성이거나 이러한 속성을 포함해야 합니다. join 절은 특별한 equals 키워드를 사용하여 지정된 키가 같은지 비교합니다. join 절에서 수행하는 모든 조인은 동등 조인입니다. join 절의 출력 모양은 수행하는 특정 조인 형식에 따라 달라집니다. 가장 일반적인 세 가지 조인 형식은 다음과 같습니다.

 

  • 내부 조인
  • 왼쪽 우선 외부 조인
  • 그룹 조인

 

1. 내부 조인 (Inner join)

 

내부 조인은 교집합과 비슷합니다. 두 데이타 원본 사이에서 일치하는 데이타들만 연결한 후 반환합니다. 내부 조인은 첫번째 데이타 원본의 데이타를 기준으로  두번째 데이타 원본이 가지고 있는 각 데이타의 특정 필드를 비교해서 일치하는 데이타들만 모아 반환합니다.

 

예를 들어 보겠습니다.

 

다음 그림에서 데이타 원본 Category는 기준이 되며, Category의 각 데이타는 Category Name, ID 를 가지고 있습니다. 그리고 Product 은 Category 에 연결할 데이타 원본이며 Product Name, CategoryID 를  갖고 있습니다. 이제 Category 의 ID 필드와 Product의 CategoryID 필드가 일치하는  데이타만 연결하면 됩니다.

 

 

내부 조인 결과에 "Grains"에 관한 데이타는 빠져 있는 것 확인 하셨나요?

내부 조인을 수행할 때 기준 데이타 원본에는 존재하지만 연결할 데이타 원본에는 존재하지 않는 데이타는 조인 결과에 포함되지 않습니다. 당연히 기준 데이타 원본에는 없지만 연결할 데이타 원본에는 존재하는 데이타의 경우에도 포함되지 않습니다.

 

내부 조인은 join 절을 통해 수행합니다. 기준 데이타 a는 from 절에서 뽑아낸 범위 변수이고, 연결 대상 데이타 b는 join 절에서 뽑아낸 변수입니다. join 절의 on 키워드는 조인 조건을 기술합니다. 이 때 on절의 조인 조건은 "동등(Equal)"만 허용합니다. "~보다 작음", "~보다 큼" 같은 비교 연산은 하락되지 않습니다. equals 키워드를 조인을 위해 따로 도입 했습니다.

 

기본 문법

 

from a in A

join b in B on a.xxxx equals b.yyyy

 

 

category와 product 클래스를 이용해서 어떻게 구현하는지는 아래와 같습니다.

        var innerJoinQuery =
            from category in categories
            join prod in products on category.ID equals prod.CategoryID
            select new { CategoryID = category.ID, CategoryName= category.Name, Product = prod.Name };

 

전체 소스

    class Program
    {
        static void Main(string[] args)
        {
            JoinDemonstration app = new JoinDemonstration();
            app.InnerJoin();

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }
    }

    class JoinDemonstration
    {
        #region Data
        class Product
        {
            public string Name { get; set; }
            public string CategoryID { get; set; }
        }

        class Category
        {
            public string Name { get; set; }
            public string ID { get; set; }
        }

        // Specify the first data source.
        List<Category> categories = new List<Category>()
        { 
            new Category(){Name="Beverages", ID="001"},
            new Category(){Name="Condiments", ID="002"},
            new Category(){Name="Vegetables", ID="003"},
            new Category(){Name="Grains", ID="004"},
            new Category(){Name="Fruit", ID="005"}            
        };

        // Specify the second data source.
        List<Product> products = new List<Product>()
       {
          new Product{Name="Cola",  CategoryID="001"},
          new Product{Name="Tea",  CategoryID="001"},
          new Product{Name="Mustard", CategoryID="002"},
          new Product{Name="Pickles", CategoryID="002"},
          new Product{Name="Carrots", CategoryID="003"},
          new Product{Name="Bok Choy", CategoryID="003"},
          new Product{Name="Peaches", CategoryID="005"},
          new Product{Name="Melons", CategoryID="005"},
        };
        #endregion

        #region InnerJoin
        public void InnerJoin()
        {
            // Create the query that selects  
            // a property from each element. 
            var innerJoinQuery =
                from category in categories
                join prod in products on category.ID equals prod.CategoryID
                select new { CategoryID = category.ID, CategoryName= category.Name, Product = prod.Name };

            Console.WriteLine("InnerJoin:");
            // Execute the query. Access results  
            // with a simple foreach statement. 
            foreach (var item in innerJoinQuery)
            {
                Console.WriteLine("{0,-10}{1,-20}{2}", item.Product, item.CategoryName, item.CategoryID);
            }
            Console.WriteLine("InnerJoin: {0} items in 1 group.", innerJoinQuery.Count());
            Console.WriteLine(System.Environment.NewLine);

        }
        #endregion
    }

 

2. 왼쪽 우선 외부 조인(Outer join)

 

외부 조인은 기본적으로 내부 조인과 비슷하지만, 다음 그림처럼 조인 결과에 기준이 되는 데이타 원본은 모두 다 포함된다는 점이 다릅니다.

 

 

 

내부 조인을 했을 때는 "004" 데이타가 조인 결과에 없었는데, 외부 조인을 했을 때는 product name 필드가 비어 있는 상태로 조인 결과에 포함됩니다. 이것은 외부 조인이 기준이 되는 데이타 원본의 모든 데이타를 조인 결과에 반드시 포함하는 특징 때문입니다. 연결할 데이타 원본에 기준 데이타 원본의 데이타와 일치하는 데이타가 없다면 그 부분은 빈 값으로 채우게 됩니다.

 

SQL에서 지원하는 외부 조인에는 왼쪽 조인(Left Join), 오른쪽 조인(Right Join), 완전 외부 조인(Full Outer Join) 이렇게 세가지가 있습니다. 왼쪽 조인은 왼쪽 데이타 원본을 기준으로 삼아 조인을 수행하고, 오른쪽 조인은 오른쪽 데이타 원본을 기준으로 삼아 조인을 수행하며, 완전 조인은 왼쪽 오른쪽 데이타 원본 모두를 기준으로 삼습니다.

 

LINQ는 왼쪽 조인만을 지원합니다.

 

외부 조인을 사용하는 밥법은 내부 조인과 크게 다르지 않습니다. 먼저 join 절을 이용해서 조인을 수행한 후 그 결과를 임시 컬렉션에 저장하고, 이 임시 컬렉션에 대해 DefaultisEmpty 연산을 수행헤서 비어 있는 조인 결과에 빈 값을 채워 넣습니다.

            // Create the query. 
            var leftOuterQuery =
               from category in categories
               join prod in products on category.ID equals prod.CategoryID into prodGroup
               select prodGroup.DefaultIfEmpty(new Product() { Name = "Nothing!", CategoryID = category.ID });

 

LeftOuterJoin() 을 JoinDemonstration 클래스에 추가하고, 메인에서 app.LeftOuterJoin() 를 호출하면 됩니다.

 

전체 소스

        public void LeftOuterJoin()
        {
            // Create the query. 
            var leftOuterQuery =
               from category in categories
               join prod in products on category.ID equals prod.CategoryID into prodGroup
               select prodGroup.DefaultIfEmpty(new Product() { Name = "Nothing!", CategoryID = category.ID });

            // Store the count of total items (for demonstration only). 
            int totalItems = 0;

            Console.WriteLine("Left Outer Join:");

            // A nested foreach statement  is required to access group items 
            foreach (var prodGrouping in leftOuterQuery)
            {
                Console.WriteLine("Group:{0,5} ea", prodGrouping.Count());
                foreach (var item in prodGrouping)
                {
                    totalItems++;
                    Console.WriteLine("  {0,-10}{1}", item.Name, item.CategoryID);
                }
                Console.WriteLine();
            }
            Console.WriteLine("LeftOuterJoin: {0} items in {1} groups", totalItems, leftOuterQuery.Count());
            Console.WriteLine(System.Environment.NewLine);
        }
        #endregion

 

 

3. 그룹 조인

 

into 식이 있는 join 절을 그룹 조인이라고 합니다.

        var groupJoinQuery =
            from category in categories
            join prod in products on category.ID equals prod.CategoryID into prodGroup
            select prodGroup;

 

그룹 조인은 왼쪽의 데이타 원본 요소를 오른쪽의 데이타 연결 요소에 있는 하나 이상의 일치하는 요소와 연결하는 계층적 결과를 생성합니다.

 

왼쪽 데이타 원본 요소와 일치하는 오른쪽 데이타 연결 요소가 없을 경우 join 절은 해당 항목에 대해 빈 배열을 생성합니다. 따라서 그룹 조인은 결과값이 그룹으로 구성된다는 점을 제외하고는 기본적으로 내부 조인입니다.

 

다음의 method를 JoinDemonstration class 에 추가하고 main에서 호출하면 됩니다.

        #region GroupJoin
        public void GroupJoin()
        {
            var groupJoinQuery =
               from category in categories
               join prod in products on category.ID equals prod.CategoryID into prodGroup
               select prodGroup;

            // Store the count of total items (for demonstration only). 
            int totalItems = 0;

            Console.WriteLine("Simple GroupJoin:");

            // A nested foreach statement is required to access group items. 
            foreach (var prodGrouping in groupJoinQuery)
            {
                Console.WriteLine("Group:");
                foreach (var item in prodGrouping)
                {
                    totalItems++;
                    Console.WriteLine("   {0,-10}{1}", item.Name, item.CategoryID);
                }
            }
            Console.WriteLine("Unshaped GroupJoin: {0} items in {1} unnamed groups", totalItems, groupJoinQuery.Count());
            Console.WriteLine(System.Environment.NewLine);
        }
        #endregion

 

 

'C# with LINQ to SQL' 카테고리의 다른 글

LINQ - 4칙 연산(select, insert, update, delete) - DataGridView 이용  (0) 2015.07.01
LINQ - 로그인창 적용  (0) 2015.06.25
LINQ - 그룹핑2  (0) 2015.06.19
LINQ - 그룹핑1  (0) 2015.06.18
LINQ - 소개  (0) 2015.06.16
posted by 따시쿵
2015. 6. 19. 17:18 C# with LINQ to SQL

앞 부분에 이어서 계속 그룹화 시키는 방법을 알아 보도록 하겠습니다. StudentClass 클래스와 데이타를 모두 같이 사용합니다.

 

예제1 - 숫자 범위를 그룹키로 사용하는 경우

 

다음 예제에서는 숫자 범위를 그룹 키로 사용하여 그룹화하는 방법을 보여 줍니다. 쿼리는 FirstName, LastName 과 학생이 속한 백분위수 범위만 포함한 Anonymous type(익명 타입)으로 결과를 반환합니다.

 

결과를 표시하기 위해 완전한 Student 개체를 사용할 필요가 없으므로 익명 형식이 사용됩니다.

GetPercentile은 학생의 평균 점수에 기초하여 백분위수를 계산하는 함수입니다. 이 메서드는 0에서 10 사이의 정수를 반환합니다.

        protected static int GetPercentile(Student s)
        {
            double avg = s.ExamScores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

 

참고로 List<T> 형식의 데이타에서 평균값을 구하는 method 가 Average() 입니다. 최고값은 Max(), 최저값은 Min(), 아이템 개수는 Count() 입니다.

 

어째든, 다음 함수가 실제적으로 그룹화를 시키는 내용입니다.

        public void GroupByRange()
        {
            Console.WriteLine("\r\n숫자 범위로 그룹화 시키기:");

            var queryNumericRange =
                from student in students
                let percentile = GetPercentile(student)
                group new { student.FirstName, student.LastName } by percentile into percentGroup
                orderby percentGroup.Key
                select percentGroup;

            // Nested foreach required to iterate over groups and group items. 
            foreach (var studentGroup in queryNumericRange)
            {
                Console.WriteLine("Key: {0}", (studentGroup.Key * 10));
                foreach (var item in studentGroup)
                {
                    Console.WriteLine("\t{0}, {1}", item.LastName, item.FirstName);
                }
            }
        }

 

전체 소스

    public class StudentClass
    {
        #region data
        protected enum GradeLevel { FirstYear = 1, SecondYear, ThirdYear, FourthYear };
        protected class Student
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int ID { get; set; }
            public GradeLevel Year;
            public List<int> ExamScores;
        }

        protected static List<string> students = new List<string>
        {
            new Student {FirstName = "Terry", LastName = "Adams", ID = 120, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 99, 82, 81, 79}},
            new Student {FirstName = "Fadi", LastName = "Fakhouri", ID = 116, Year = GradeLevel.ThirdYear,ExamScores = new List<int>{ 99, 86, 90, 94}},
            new Student {FirstName = "Hanying", LastName = "Feng", ID = 117, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 93, 92, 80, 87}},
            new Student {FirstName = "Cesar", LastName = "Garcia", ID = 114, Year = GradeLevel.FourthYear,ExamScores = new List<int>{ 97, 89, 85, 82}},
            new Student {FirstName = "Debra", LastName = "Garcia", ID = 115, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 35, 72, 91, 70}},
            new Student {FirstName = "Hugo", LastName = "Garcia", ID = 118, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 92, 90, 83, 78}},
            new Student {FirstName = "Sven", LastName = "Mortensen", ID = 113, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 88, 94, 65, 91}},
            new Student {FirstName = "Claire", LastName = "O'Donnell", ID = 112, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 75, 84, 91, 39}},
            new Student {FirstName = "Svetlana", LastName = "Omelchenko", ID = 111, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 97, 92, 81, 60}},
            new Student {FirstName = "Lance", LastName = "Tucker", ID = 119, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 68, 79, 88, 92}},
            new Student {FirstName = "Michael", LastName = "Tucker", ID = 122, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 94, 92, 91, 91}},
            new Student {FirstName = "Eugene", LastName = "Zabokritski", ID = 121, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 96, 85, 91, 60}}
        };
        #endregion

        //Helper method, used in GroupByRange. 
        protected static int GetPercentile(Student s)
        {
            double avg = s.ExamScores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }
        #region GroupByRange
        public void GroupByRange()
        {
            Console.WriteLine("\r\n숫자 범위로 그룹화 시키기:");

            var queryNumericRange =
                from student in students
                let percentile = GetPercentile(student)
                group new { student.FirstName, student.LastName } by percentile into percentGroup
                orderby percentGroup.Key
                select percentGroup;

            // Nested foreach required to iterate over groups and group items. 
            foreach (var studentGroup in queryNumericRange)
            {
                Console.WriteLine("Key: {0}", (studentGroup.Key * 10));
                foreach (var item in studentGroup)
                {
                    Console.WriteLine("\t{0}, {1}", item.LastName, item.FirstName);
                }
            }
        }
        #endregion
    }

    class Program
    {
        static void Main(string[] args)
        {
            StudentClass sc = new StudentClass();
            sc.GroupByRange();

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }
    }

 

예제2 - 비교 연산자를 이용하여 그룹화 시키는 방법

 

다음 예제에서는 부울 비교 식을 사용하여 소스 요소를 그룹화하는 방법을 보여 줍니다.

 

이 예제에서 부울 식은 학생의 평균 시험 점수가 75점을 넘는지 확인합니다. 위의 예제와 같이 완전한 소스 요소가 필요하지 않으므로 결과는 익명 형식으로 반환됩니다. 익명 형식의 속성은 Key 멤버의 속성이 되고 쿼리 실행 시에 이름으로 액세스할 수 있습니다.

 

결론은 평균 점수가 75점을 넘는지,못 넘는지를 넘는지를 기준으로 두 그룹으로 나뉘는 쿼리문입니다. 비교의 기준은 by 절에 있는 by student.ExamScores.Average() > 75 절입니다.

public void GroupByBoolean()
{            
    Console.WriteLine("\r\nGroup by a Boolean into two groups with string keys");
    Console.WriteLine("\"True\" and \"False\" and project into a new anonymous type:");
    var queryGroupByAverages = from student in students
                               group new { student.FirstName, student.LastName }
                                    by student.ExamScores.Average() > 75 into studentGroup
                               select studentGroup;

    foreach (var studentGroup in queryGroupByAverages)
    {
        Console.WriteLine("Key: {0}", studentGroup.Key);
        foreach (var student in studentGroup)
            Console.WriteLine("\t{0} {1}", student.FirstName, student.LastName);
    }            
}

 

전체 소스

    public class StudentClass
    {
        #region data
        protected enum GradeLevel { FirstYear = 1, SecondYear, ThirdYear, FourthYear };
        protected class Student
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int ID { get; set; }
            public GradeLevel Year;
            public List<int> ExamScores;
        }

        protected static List<Student> students = new List<Student>
        {
            new Student {FirstName = "Terry", LastName = "Adams", ID = 120, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 99, 82, 81, 79}},
            new Student {FirstName = "Fadi", LastName = "Fakhouri", ID = 116, Year = GradeLevel.ThirdYear,ExamScores = new List<int>{ 99, 86, 90, 94}},
            new Student {FirstName = "Hanying", LastName = "Feng", ID = 117, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 93, 92, 80, 87}},
            new Student {FirstName = "Cesar", LastName = "Garcia", ID = 114, Year = GradeLevel.FourthYear,ExamScores = new List<int>{ 97, 89, 85, 82}},
            new Student {FirstName = "Debra", LastName = "Garcia", ID = 115, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 35, 72, 91, 70}},
            new Student {FirstName = "Hugo", LastName = "Garcia", ID = 118, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 92, 90, 83, 78}},
            new Student {FirstName = "Sven", LastName = "Mortensen", ID = 113, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 88, 94, 65, 91}},
            new Student {FirstName = "Claire", LastName = "O'Donnell", ID = 112, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 75, 84, 91, 39}},
            new Student {FirstName = "Svetlana", LastName = "Omelchenko", ID = 111, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 97, 92, 81, 60}},
            new Student {FirstName = "Lance", LastName = "Tucker", ID = 119, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 68, 79, 88, 92}},
            new Student {FirstName = "Michael", LastName = "Tucker", ID = 122, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 94, 92, 91, 91}},
            new Student {FirstName = "Eugene", LastName = "Zabokritski", ID = 121, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 96, 85, 91, 60}}
        };
        #endregion

        //Helper method, used in GroupByRange. 
        protected static int GetPercentile(Student s)
        {
            double avg = s.ExamScores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }

        #region GroupByBoolean
        public void GroupByBoolean()
        {
            Console.WriteLine("\r\nGroup by a Boolean into two groups with string keys");
            Console.WriteLine("\"True\" and \"False\" and project into a new anonymous type:\n");
            var queryGroupByAverages = from student in students
                                       group new { student.FirstName, student.LastName }
                                            by student.ExamScores.Average() > 75 into studentGroup
                                       select studentGroup;

            foreach (var studentGroup in queryGroupByAverages)
            {
                Console.WriteLine("Key: {0}", studentGroup.Key);

                foreach (var student in studentGroup)
                    Console.WriteLine("\t{0} {1}", student.FirstName, student.LastName);
            }
        }
        #endregion
    }

    class Program
    {
        static void Main(string[] args)
        {
            StudentClass sc = new StudentClass();
            sc.GroupByBoolean();

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }
    }

 

posted by 따시쿵
2015. 6. 18. 10:44 C# with LINQ to SQL

이번 포스트에서는 Group by 로 데이타 분류하기를 다뤄보도록 하겠습니다.

 

기본 문법

 

group A by B into C

 

A 에는 from 절에서 뽑아낸 범위 범수  

B 에는 분류 기준

C 에는 그룹 변수를 위치 시킵니다.

 

 

예제1

 

학생들의 점수를 나타내는 클래스를 대상으로 80점 미만인가? 80점 이상인가를 group by 절을 이용해서 작성해 보도록 하겠습니다.

    class Profile
    {
        public string Name { get; set; }
        public int Score { get; set; }
    }

    Profile[] arrProflie = {
                                new Profile(){Name="이순신",    Score=65},
                                new Profile(){Name="을지문덕",  Score=88},
                                new Profile(){Name="강감찬",    Score=79},
                                new Profile(){Name="세종대왕",  Score=59},
                                new Profile(){Name="장영실",    Score=90}
                            };

 

이 데이타를 group by 절을 이용해서 분류해 보겠습니다.

    var listProfile = from profile in arrProflie
                      orderby profile.Score
                      group profile by profile.Score < 80 into g
                      select new { GroupKey = g.Key, Profiles = g };

 

위의 쿼리식의 그룹 변수 g에는 Score 값이 80점 미만인 객체의 컬렉션, 80점 이상인 객체의 컬렉센이 입력되고, select 문이 추출하는 새로운 무명 향식은 컬렉션의 컬렉션이 됩니다. 그리고 이 무명형식의 Profiles 필드는 바로 이 그룹 변수 g를 담게 됩니다. 정렬은 점수 오름차순입니다. 최종적으로 쿼리의 결과를 다담은 listProfiles은 다음과 같은 모습을 할 것입니다.

 

 

전체 소스

    class Profile
    {
        public string Name { get; set; }
        public int Score { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Profile[] arrProflie = {
                                        new Profile(){Name="이순신",    Score=65},
                                        new Profile(){Name="을지문덕",  Score=88},
                                        new Profile(){Name="강감찬",    Score=79},
                                        new Profile(){Name="세종대왕",  Score=59},
                                        new Profile(){Name="장영실",    Score=90}
                                    };

            var listProfile = from profile in arrProflie
                              orderby profile.Score
                              group profile by profile.Score < 80 into g
                              select new { GroupKey = g.Key, Profiles = g };

            foreach(var Group in listProfile)
            {
                Console.WriteLine(" - 80점 미만? : {0}", Group.GroupKey);

                foreach(var profile in Group.Profiles)
                {
                    Console.WriteLine("{0}, {1}", profile.Name, profile.Score);
                }

                Console.WriteLine();
            }

            Console.ReadLine();
        }
    }

 

 

 

예제2 -  단일 속성을 그룹키로 사용하여 그룹화 시키기

 

학년별 학생들의 점수를 표시하기 위해 StudentClass 를 정의 합니다. 그리고 데이타도 같이 추가해 줍니다.

    public class StudentClass
    {
        #region data
        protected enum GradeLevel { FirstYear = 1, SecondYear, ThirdYear, FourthYear };
        protected class Student
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int ID { get; set; }
            public GradeLevel Year;
            public List<int> ExamScores;
        }

        protected static List<Student> = new List<Student>
        {
            new Student {FirstName = "Terry", LastName = "Adams", ID = 120, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 99, 82, 81, 79}},
            new Student {FirstName = "Fadi", LastName = "Fakhouri", ID = 116, Year = GradeLevel.ThirdYear,ExamScores = new List<int>{ 99, 86, 90, 94}},
            new Student {FirstName = "Hanying", LastName = "Feng", ID = 117, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 93, 92, 80, 87}},
            new Student {FirstName = "Cesar", LastName = "Garcia", ID = 114, Year = GradeLevel.FourthYear,ExamScores = new List<int>{ 97, 89, 85, 82}},
            new Student {FirstName = "Debra", LastName = "Garcia", ID = 115, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 35, 72, 91, 70}},
            new Student {FirstName = "Hugo", LastName = "Garcia", ID = 118, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 92, 90, 83, 78}},
            new Student {FirstName = "Sven", LastName = "Mortensen", ID = 113, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 88, 94, 65, 91}},
            new Student {FirstName = "Claire", LastName = "O'Donnell", ID = 112, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 75, 84, 91, 39}},
            new Student {FirstName = "Svetlana", LastName = "Omelchenko", ID = 111, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 97, 92, 81, 60}},
            new Student {FirstName = "Lance", LastName = "Tucker", ID = 119, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 68, 79, 88, 92}},
            new Student {FirstName = "Michael", LastName = "Tucker", ID = 122, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 94, 92, 91, 91}},
            new Student {FirstName = "Eugene", LastName = "Zabokritski", ID = 121, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 96, 85, 91, 60}}
        };
        #endregion
    }

 

예제에서는 학생들의 LastName 을 키로 사용합니다. 또한 부분 문자열을 키로 사용할 수도 있습니다. 그룹화 작업은 형식에 대해 같은 연산자(equal, ==)를 사용합니다.

 

아래는 실제로 그룹화 작업을 수행하는 method 입니다. Group의 변수는 IGroup<T> 형식입니다.

        public void GroupBySingleProperty()
        {
            Console.WriteLine("Group by a single property in an object:\n");

            // Variable queryLastNames is an IEnumerable<IGROUPING<string, DataClass.Student>>.  
            var queryLastNames =
                from student in students
                group student by student.LastName into newGroup
                orderby newGroup.Key
                select newGroup;

            foreach (var nameGroup in queryLastNames)
            {
                Console.WriteLine("Key: {0}", nameGroup.Key);
                foreach (var student in nameGroup)
                {
                    Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
                }
            }
        }

 

전체 소스

    public class StudentClass
    {
        #region data
        protected enum GradeLevel { FirstYear = 1, SecondYear, ThirdYear, FourthYear };
        protected class Student
        {
            public string FirstName { get; set; }
            public string LastName { get; set; }
            public int ID { get; set; }
            public GradeLevel Year;
            public List<int> ExamScores;
        }

        protected static List<Student> students = new List<Student>
        {
            new Student {FirstName = "Terry", LastName = "Adams", ID = 120, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 99, 82, 81, 79}},
            new Student {FirstName = "Fadi", LastName = "Fakhouri", ID = 116, Year = GradeLevel.ThirdYear,ExamScores = new List<int>{ 99, 86, 90, 94}},
            new Student {FirstName = "Hanying", LastName = "Feng", ID = 117, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 93, 92, 80, 87}},
            new Student {FirstName = "Cesar", LastName = "Garcia", ID = 114, Year = GradeLevel.FourthYear,ExamScores = new List<int>{ 97, 89, 85, 82}},
            new Student {FirstName = "Debra", LastName = "Garcia", ID = 115, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 35, 72, 91, 70}},
            new Student {FirstName = "Hugo", LastName = "Garcia", ID = 118, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 92, 90, 83, 78}},
            new Student {FirstName = "Sven", LastName = "Mortensen", ID = 113, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 88, 94, 65, 91}},
            new Student {FirstName = "Claire", LastName = "O'Donnell", ID = 112, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 75, 84, 91, 39}},
            new Student {FirstName = "Svetlana", LastName = "Omelchenko", ID = 111, Year = GradeLevel.SecondYear, ExamScores = new List<int>{ 97, 92, 81, 60}},
            new Student {FirstName = "Lance", LastName = "Tucker", ID = 119, Year = GradeLevel.ThirdYear, ExamScores = new List<int>{ 68, 79, 88, 92}},
            new Student {FirstName = "Michael", LastName = "Tucker", ID = 122, Year = GradeLevel.FirstYear, ExamScores = new List<int>{ 94, 92, 91, 91}},
            new Student {FirstName = "Eugene", LastName = "Zabokritski", ID = 121, Year = GradeLevel.FourthYear, ExamScores = new List<int>{ 96, 85, 91, 60}}
        };
        #endregion

        //Helper method, used in GroupByRange. 
        protected static int GetPercentile(Student s)
        {
            double avg = s.ExamScores.Average();
            return avg > 0 ? (int)avg / 10 : 0;
        }        

        public void QueryHighScores(int exam, int score)
        {
            var highScores = from student in students
                             where student.ExamScores[exam] > score
                             select new { Name = student.FirstName, Score = student.ExamScores[exam] };

            foreach (var item in highScores)
            {
                Console.WriteLine("{0,-15}{1}", item.Name, item.Score);
            }
        }

        public void GroupBySingleProperty()
        {
            Console.WriteLine("single property[LastName] 로 그룹화 시키기:\n");

            // Variable queryLastNames is an IEnumerable<IGrouping<string,  
            // DataClass.Student>>.  
            var queryLastNames =
                from student in students
                group student by student.LastName into newGroup
                orderby newGroup.Key
                select newGroup;

            foreach (var nameGroup in queryLastNames)
            {
                Console.WriteLine("Key: {0}", nameGroup.Key);
                foreach (var student in nameGroup)
                {
                    Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
                }
            }
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            StudentClass sc = new StudentClass();
            sc.GroupBySingleProperty();

            // Keep the console window open in debug mode.
            Console.WriteLine("Press any key to exit");
            Console.ReadKey();
        }
    }

 

 

 

예제3 - 개체의 속성(property)이 아닌 항목을 그룹 키로 사용하는 경우

 

위에서 사용한 StudentClass 를 계속 사용합니다.

 

이 예제에서 키는 학생의 LastName 중에서 첫 번째 문자입니다. 첫번째 문자를 가지고 그룹화를 시키는 method 는 아래와 같습니다.

        public void GroupBySubstring()
        {
            Console.WriteLine("\r\nLastName 의 첫글자를 가지고 그룹화 사키기:");

            var queryFirstLetters =
                from student in students
                group student by student.LastName[0] into newGroup
                orderby newGroup.Key
                select newGroup;

            foreach (var studentGroup in queryFirstLetters)
            {
                Console.WriteLine("Key: {0}", studentGroup.Key);
                // Nested foreach is required to access group items. 
                foreach (var student in studentGroup)
                {
                    Console.WriteLine("\t{0}, {1}", student.LastName, student.FirstName);
                }
            }
        }

 

 

posted by 따시쿵
2015. 6. 16. 10:49 C# with LINQ to SQL

LINQ 는 Language INtegrated Query위 약어로, 컬렉션을 편리하게 다루기 위한 목적으로 만들어진 질의 언어입니다. C# 3.0 버전에서부터 탑재한 LINQ 덕분에 프로그래머들은 데이타를 찾고 병합하고 정렬하는 코드를 작성하는 짐을 상당 부분 내려 놓을 수 있게 되었습니다.

 

기본 문법

 

  • From : 어떤 데이타 집합에서 찾을 것인가?
  • Where : 어떤 값의 데이타를 찾을 것인가? (필터 역할)
  • Orderby : 어떤 데이타 순서대로 나열할 것인가?(순서 정하기)
  • Select : 어떤 항목을 추출할 것인가?

 

예제 1 - LINQ 문을 사용하지 않고 프로그램 작성

 

키가 175 미만인 데이타만을 골라서 새 컬렉션으로 추출하는 프로그램

    class Profile
    {
        public string Name { get; set; }
        public int Height { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Profile[] arrProfile = {
                                       new Profile{Name = "이순신", Height=210},
                                       new Profile{Name = "강감찬", Height=169},
                                       new Profile{Name = "을지문덕", Height=175},
                                       new Profile{Name = "세종대왕", Height=183},
                                       new Profile{Name = "안창호", Height=159},
                                       new Profile{Name = "연개소문", Height=172}
                                   };

            // 1단계 - 키가 175 미만인 데이타를 추출함
            List<profile> profiles = new List<Profile>();
            foreach(Profile profile in arrProfile)
            {
                if (profile.Height < 175)
                    profiles.Add(profile);
            }

            // 2단계 - 키의 오름차순으로 정열함
            profiles.Sort(
                (profile1, profile2) =>  { return profile1.Height - profile2.Height;  }
            );

            // 3단계 - 출력함
            foreach(var profile in profiles)
                Console.WriteLine("{0}, {1}", profile.Name, profile.Height);

            Console.ReadLine();
        }
    }

 

 

예제 2 - LINQ 문을 사용하여 프로그램 작성

 

키가 175 미만인 데이타만을 골라서 새 컬렉션으로 추출하는 프로그램

    class Profile
    {
        public string Name { get; set; }
        public int Height { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Profile[] arrProfile = {
                                       new Profile{Name = "이순신", Height=210},
                                       new Profile{Name = "강감찬", Height=169},
                                       new Profile{Name = "을지문덕", Height=175},
                                       new Profile{Name = "세종대왕", Height=183},
                                       new Profile{Name = "안창호", Height=159},
                                       new Profile{Name = "연개소문", Height=172}
                                   };

            // 1단계 - 키가 175 미만인 데이타를 추출함
            var profiles = from profile in arrProfile
                           where profile.Height < 175
                           orderby profile.Height ascending
                           select profile;

            // 2단계 - 출력함
            foreach (var profile in profiles)
                Console.WriteLine("{0}, {1}", profile.Name, profile.Height);

            Console.ReadLine();
        }
    }


예제3 - select 문의 분석


select 절은 다음과 같이 사용하면 됩니다.

var profiles = from profile in arrProfile
            where profile.Height < 175
            orderby profile.Height ascending
            select profile;

위의 예에서 var 형식으로 선언된 profiles 의 실제 형식이 궁금할 겁니다. C# 컴파일러가 var 형식을 LINQ 쿼리식이 반환할 결과 형식에 맞춰 알아서 컴파일 해 주기는 하지만, 실제로 var 가 어떤 형식으로 치환되는지를 알아둘 필요가 있습니다. LINQ 질의 결과는 IEnumerable<T> 로 치환되는데, 이 형식 매개변수 T는 select 문에 의해 결정됩니다. 


예를 들어 위 LINQ 쿼리식은 배열로부터 175 미만인 Profile 객체를 골라내는데, 그 결과는 IEnumerable<Profile> 형식이 됩니다. 다음과 같이 select 문에서 Profile 객체 전체가 아닌 Name 프로퍼티만 추출하면 profiles는 IEnumerable<string> 형식으로 컴파일됩니다.

var profiles = from profile in arrProfile
            where profile.Height < 175
            orderby profile.Height ascending
            select profile.Name;

추가적으로 select 문은 무명 형식(Anonymous type)을 이용해서 다음과 같이 새로운 형식을 만들어 낼 수도 있습니다.

var profiles = from profile in arrProfile
            where profile.Height < 175
            orderby profile.Height ascending
            select new { Name = profile.Name, InchHeight = profile.Height * 0.393 };

전체 소스

    class Profile
    {
        public string Name { get; set; }
        public int Height { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Profile[] arrProfile = {
                                       new Profile{Name = "이순신", Height=210},
                                       new Profile{Name = "강감찬", Height=169},
                                       new Profile{Name = "을지문덕", Height=175},
                                       new Profile{Name = "세종대왕", Height=183},
                                       new Profile{Name = "안창호", Height=159},
                                       new Profile{Name = "연개소문", Height=172}
                                   };

            // 1단계 - 키가 175 미만인 데이타를 추출함
            var profiles = from profile in arrProfile
                           where profile.Height < 175
                           orderby profile.Height ascending
                           select new { Name = profile.Name, InchHeight = profile.Height * 0.393 };

            // 2단계 - 출력함
            foreach (var profile in profiles)
                Console.WriteLine("{0}, {1}", profile.Name, profile.InchHeight);

            Console.ReadLine();
        }
    }



 

예제4 - where 조건에 and, or 연산자 사용하기 / 여러개의 데이타 원본에 질의하기

 

LINQ 쿼리식은 데이타 원본에 접근하기 위해 from 절을 사용합니다. 여러개의 데이타 원본에 접근하려면 from 절을 중첩해서 사용하면 됩니다.

 

foreach 문을 중첩해서 사용하는 것처럼 말입니다.

 

다음의 클래스는 각 학년별, 학급별, 점수를 나타내며, 과목별 점수를 담기 위해 Score 필드를 배열로 선언했습니다.

    class GradeClassScore
    {
        public int Grade { get; set; }
        public string ClassName { get; set; }
        public int[] Score { get; set; }
    }

 

그리고 이 클래스를 바탕으로 배열 하나를 다음과 같이 선언했습니다.

            GradeClassScore[] arrClass = {
                                             new GradeClassScore(){Grade = 1, ClassName = "연두반", Score = new int[]{10, 20, 30, 40, 50}},
                                             new GradeClassScore(){Grade = 1, ClassName = "분홍반", Score = new int[]{65, 75, 85, 95, 100}},
                                             new GradeClassScore(){Grade = 1, ClassName = "파랑반", Score = new int[]{68, 28, 38, 48, 58}},
                                             new GradeClassScore(){Grade = 1, ClassName = "노랑반", Score = new int[]{83, 84, 85, 86, 86}},
                                             new GradeClassScore(){Grade = 1, ClassName = "보라반", Score = new int[]{87, 87, 87, 87, 87}},
                                             new GradeClassScore(){Grade = 2, ClassName = "연두반", Score = new int[]{10, 20, 30, 40, 50}},
                                             new GradeClassScore(){Grade = 2, ClassName = "분홍반", Score = new int[]{65, 75, 85, 95, 100}},
                                             new GradeClassScore(){Grade = 2, ClassName = "파랑반", Score = new int[]{68, 28, 38, 48, 58}},
                                             new GradeClassScore(){Grade = 2, ClassName = "노랑반", Score = new int[]{83, 84, 85, 86, 86}},
                                             new GradeClassScore(){Grade = 2, ClassName = "보라반", Score = new int[]{87, 87, 87, 87, 87}},
                                             new GradeClassScore(){Grade = 3, ClassName = "연두반", Score = new int[]{10, 20, 30, 40, 50}},
                                             new GradeClassScore(){Grade = 3, ClassName = "분홍반", Score = new int[]{65, 75, 85, 95, 100}},
                                             new GradeClassScore(){Grade = 3, ClassName = "파랑반", Score = new int[]{68, 28, 38, 48, 58}},
                                             new GradeClassScore(){Grade = 3, ClassName = "노랑반", Score = new int[]{83, 84, 85, 86, 86}},
                                             new GradeClassScore(){Grade = 3, ClassName = "보라반", Score = new int[]{87, 87, 87, 87, 87}}
                                         };

 

이제 준비는 마쳤습니다. 위 배열에서 1학년 중에서 점수가 60점 미만인 학급과 과목 점수를 중첩 from 절을 이용해서 뽑아 보겠습니다. 그리고 여기서 한가지 팁을 더 알려 드리자면 where 절에 and, or 를 포함 시킬 수 있습니다. 즉 and => &&, or => || 를 사용하시면 됩니다.

            var classes = from c in arrClass
                            from s in c.Score
                            where s < 60 && c.Grade == 1
                            orderby s
                          select new { c.ClassName, Lowest = s };

전체 소스

    class GradeClassScore
    {
        public int Grade { get; set; }
        public string ClassName { get; set; }
        public int[] Score { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            GradeClassScore[] arrClass = {
                                             new GradeClassScore(){Grade = 1, ClassName = "연두반", Score = new int[]{10, 20, 30, 40, 50}},
                                             new GradeClassScore(){Grade = 1, ClassName = "분홍반", Score = new int[]{65, 75, 85, 95, 100}},
                                             new GradeClassScore(){Grade = 1, ClassName = "파랑반", Score = new int[]{68, 28, 38, 48, 58}},
                                             new GradeClassScore(){Grade = 1, ClassName = "노랑반", Score = new int[]{83, 84, 85, 86, 86}},
                                             new GradeClassScore(){Grade = 1, ClassName = "보라반", Score = new int[]{87, 87, 87, 87, 87}},
                                             new GradeClassScore(){Grade = 2, ClassName = "연두반", Score = new int[]{10, 20, 30, 40, 50}},
                                             new GradeClassScore(){Grade = 2, ClassName = "분홍반", Score = new int[]{65, 75, 85, 95, 100}},
                                             new GradeClassScore(){Grade = 2, ClassName = "파랑반", Score = new int[]{68, 28, 38, 48, 58}},
                                             new GradeClassScore(){Grade = 2, ClassName = "노랑반", Score = new int[]{83, 84, 85, 86, 86}},
                                             new GradeClassScore(){Grade = 2, ClassName = "보라반", Score = new int[]{87, 87, 87, 87, 87}},
                                             new GradeClassScore(){Grade = 3, ClassName = "연두반", Score = new int[]{10, 20, 30, 40, 50}},
                                             new GradeClassScore(){Grade = 3, ClassName = "분홍반", Score = new int[]{65, 75, 85, 95, 100}},
                                             new GradeClassScore(){Grade = 3, ClassName = "파랑반", Score = new int[]{68, 28, 38, 48, 58}},
                                             new GradeClassScore(){Grade = 3, ClassName = "노랑반", Score = new int[]{83, 84, 85, 86, 86}},
                                             new GradeClassScore(){Grade = 3, ClassName = "보라반", Score = new int[]{87, 87, 87, 87, 87}}
                                         };

            var classes = from c in arrClass
                            from s in c.Score
                            where s < 60 && c.Grade == 1
                            orderby s
                          select new { c.ClassName, Lowest = s };

            foreach(var c in classes)
                Console.WriteLine("낙제 : {0} ({1})", c.ClassName, c.Lowest);

            Console.ReadLine();

        }
    }

 

 

posted by 따시쿵
2015. 6. 11. 16:15 C#

 

3. LINQ를 이용한 방법으로 표현하는 경우

using System;
using System.Collections.Generic;
using System.Linq;

namespace MylambdaExpression11
{
    class Program
    {
        static void Main(string[] args)
        {
            // 예제 - 1 
            // 다음 예제에서는 Func<T, TResult> 델리게이트를 선언하고 사용하는 방법을 보여 줍니다. 
            // 이 예제에서는 Func<T, TResult> 변수를 선언하고 문자열의 문자를 대문자로 변환하는 람다 식을 할당합니다. 
            // 이 메서드를 캡슐화하는 대리자는 이후에 문자열 배열의 문자열을 대문자로 변환하기 
            // 위해 Enumerable.Select 메서드에 전달됩니다. 

            Func<string, string> selector = (string s) => s.ToUpper();

            string[] words = {"orange", "apple", "Article", "elephant", "korea", "komani" };

            IEnumerable<string> aWords = words.Select(selector);

            foreach(string word in aWords)
                Console.WriteLine(word);

            Console.WriteLine();

            // 에제 - 2
            // words 배열에서 소문자 "k"로 시작하는 단어를 찾아서 알려줍니다.
            int count = words.Count(s => s.StartsWith("k"));
            Console.WriteLine("count = {0}", count);

            Console.ReadLine();
        }
    }
}

 

실행화면 입니다.

 

'C#' 카테고리의 다른 글

람다식(Lambda Expression) - 3  (0) 2015.06.09
람다식(Lambda Expression) - 2  (0) 2015.06.09
람다식(Lambda Expression) - 1  (0) 2015.06.08
EventHandler 와 EventHandler<TEventArgs> Delegate  (0) 2015.06.04
Event 와 Delegate  (0) 2015.06.01
posted by 따시쿵
2015. 6. 9. 16:35 C#

 

2. Func와 Action으로 더 간편하게 무명 함수 만드는 경우

 

익명 메서드와 무명 함수는 코드를 더 간결하게 만들어주는 요소들입니다. 하지만 이들을 선언하기 전에 해야 하는 일들을 생긱해 보면 익명 메소드를 만들기위해서 매번 델리게이트를 선언합니다. 이 문제를 해결하기 위해 마이크로소프트는 Func와 Action 델리게이트를 미리 선언해 두었습니다.Func 델리게이트는 결과를 반환하는 메소드를 참조하기 위해, Action 델리게이트는 결과를 반환하지 않는 메소드를 참조합니다.

 

 

2. Action 델리게이트

 

시스템이 입력 파라미터가 17개인 델리게이트를 미리 정의해 두었습니다. 

 

Action<T> 델리게이트

 

매개 변수가 하나이고 TResult 매개 변수에 지정된 형식의 값을 반환하는 메서드를 캡슐화 합니다.

 

 

구문

 

public delegate TResult Func<in T, out TResult>(
     T arg
)


형식 매개 변수

 

in T
      이 대리자로 캡슐화되는 메서드의 매개 변수 형식입니다.
      이 형식 매개 변수는 반공변입니다.

      즉, 지정한 형식이나 더 적게 파생되는 모든 형식을 사용할 수 있습니다.

 

설명

 

Action<T> 델리게이트를 사용하면 사용자 지정 대리자를 명시적으로 선언하지 않고도 메서드를 매개 변수로 전달할 수 있습니다.

 

캡슐화된 메서드는 이 대리자에 의해 정의되는 메서드 시그니처(파리미터 타입과 갯수)와 일치해야 합니다.

즉, 캡슐화된 메서드에는 값으로 전달되는 매개 변수 하나가 있어야 하고 값을 반환하지 않아야 합니다.

 

C#의 경우 이 메서드는 void를 반환해야 합니다. 무시되는 값을 반환하는 메서드일 수도 있습니다. 일반적으로 이러한 메서드는 작업을 수행하는 데 사용됩니다.

 

 

예제1

 

Action<T> 델리게이트를 사용하는 경우에는 매개 변수가 하나인 메서드를 캡슐화하는 대리자를 명시적으로 정의할 필요가 없습니다.

 

예를 들어, 다음 코드에서는 DisplayMessage라는 델리게이트를 명시적으로 선언하고 WriteLine 메서드 또는 ShowWindowsMessage 메서드에 대한 참조를 해당 델리게이트 인스턴스에 할당합니다.

    delegate void DisplayMessage(string message);
    class Program
    {
        static void Main(string[] args)
        {
            DisplayMessage messageTarget;

            if (Environment.GetCommandLineArgs().Length > 1)
                messageTarget = ShowWindowMessage;
            else
                messageTarget = Console.WriteLine;

            messageTarget("Hi, Korea world !!!");

            Console.ReadLine();
        }

        static void ShowWindowMessage(string message)
        {
            MessageBox.Show(message);
        }
    }

 

예제2

 

다음 예제에서는 새 델리게이트를 명시적으로 정의하고 명명된 메서드를 할당하는 대신 Action<T> 델리게이트를 인스턴스화하여 이 코드를 간소화합니다.

    class Program
    {
        static void Main(string[] args)
        {
            Action<string> messageTarget;

            if (Environment.GetCommandLineArgs().Length > 1)
                messageTarget = ShowWindowMessage;
            else
                messageTarget = Console.WriteLine;

            messageTarget("Hi, Korea world !!!");

            Console.ReadLine();
        }

        static void ShowWindowMessage(string message)
        {
            MessageBox.Show(message);
        }

예제3

 

Action<T> 델리게이트를 anonymous method와 함께 사용할 수도 있습니다.

    class Program
    {
        static void Main(string[] args)
        {
            Action<string> messageTarget;

            if (Environment.GetCommandLineArgs().Length > 1)
                messageTarget = delegate(string s) { ShowWindowMessage(s); };
            else
                messageTarget = delegate(string s) { Console.WriteLine(s); };

            messageTarget("Hi, Korea world !!!");

            Console.ReadLine();
        }

        static void ShowWindowMessage(string message)
        {
            MessageBox.Show(message);
        }

 

예제4

 

Action<T> 델리게이트 인스턴스에 람다 식을 할당할 수도 있습니다.

    class Program
    {
        static void Main(string[] args)
        {
            Action<string> messageTarget;

            if (Environment.GetCommandLineArgs().Length > 1)
                messageTarget = (string s) => { ShowWindowMessage(s); };
            else
                messageTarget = (string s) => { Console.WriteLine(s); };

            messageTarget("Hi, Korea world !!!");

            Console.ReadLine();
        }

        static void ShowWindowMessage(string message)
        {
            MessageBox.Show(message);
        }

 

 

실행화면은 아래와 같이 두 가지 화면이 동일하게 나옵니다. 

 

 

 

'C#' 카테고리의 다른 글

람다식(Lambda Expression) - 4  (0) 2015.06.11
람다식(Lambda Expression) - 2  (0) 2015.06.09
람다식(Lambda Expression) - 1  (0) 2015.06.08
EventHandler 와 EventHandler<TEventArgs> Delegate  (0) 2015.06.04
Event 와 Delegate  (0) 2015.06.01
posted by 따시쿵
2015. 6. 9. 11:04 C#

 

2. Func와 Action으로 더 간편하게 무명 함수 만드는 경우

 

익명 메서드와 무명 함수는 코드를 더 간결하게 만들어주는 요소들입니다. 하지만 이들을 선언하기 전에 해야 하는 일들을 생긱해 보면 익명 메소드를 만들기위해서 매번 델리게이트를 선언합니다. 이 문제를 해결하기 위해 마이크로소프트는 Func와 Action 델리게이트를 미리 선언해 두었습니다.Func 델리게이트는 결과를 반환하는 메소드를 참조하기 위해, Action 델리게이트는 결과를 반환하지 않는 메소드를 참조합니다.

 

 

1. Func 델리게이트

 

.Net 프레임워크에서는 모두 17가지의 Func 델리게이트가 미리 준비되어 있습니다.

입력 파라미터는 최대 16개까지 사용 가능합니다.

 

 

Func<T, TResult> 델리게이트

 

 

구문

 

public delegate TResult Func<in T, out TResult>(
 T arg
)

 

형식 매개 변수

 

in T

 

     이 대리자로 캡슐화되는 메서드의 매개 변수 형식입니다.

     이 형식 매개 변수는 반공변입니다.

     즉, 지정한 형식이나 더 적게 파생되는 모든 형식을 사용할 수 있습니다.

 

out TResult

 

     이 대리자로 캡슐화되는 메서드의 반환 값 형식입니다.

     이 형식 매개 변수는 공변입니다.

     즉, 지정한 형식이나 더 많이 파생되는 모든 형식을 사용할 수 있습니다.

 

 

매개 변수

 

arg
      형식: T
      이 대리자로 캡슐화되는 메서드의 매개 변수입니다.

 

반환 값
     형식: TResult
     이 대리자로 캡슐화되는 메서드의 반환 값입니다.

 


설명

 

1. 이 대리자를 사용하면 사용자 지정 대리자를 명시적으로 선언하지 않고 매개 변수로 전달할 수 있는 메서드를 나타낼 수 있습니다.

 

2. 캡슐화된 메서드는 이 델리게이트에 의해 정의되는 메서드 시그니처(파라미터 타입과 갯수)와 일치해야 합니다. 즉, 캡슐화된 메서드에는 값으로 전달되는 매개 변수 하나가 있어야 하고 값을 반환해야 합니다.

 

 

예제1

 

ConvertMethod라는 델리게이트를 명시적으로 선언하고 UppercaseString 메서드에 대한 참조를 해당 델리게이트 인스턴스에 할당합니다.

    delegate string ConvertMethod(string inString);
    class Program
    {
        static void Main(string[] args)
        {
            ConvertMethod convertMeth = UppercaseString;
            string name = "Korea Forever !!!!";

            Console.WriteLine("Before convert string : {0}", name);
            Console.WriteLine("After convert string : {0}", convertMeth(name));

            Console.ReadLine();
        }

        static string  UppercaseString(string inputString)
        {
            return inputString.ToUpper();
        }
    }

 

에제2

 

다음 예제에서는 새 델리게이트를 명시적으로 정의하고 명명된 메서드를 할당하는 대신 Func<T, TResult> 델리게이트를 인스턴스화하여 이 코드를 간소화합니다.

        static void Main(string[] args)
        {
            Func<string, string> convertMethod = UppercaseString;
            string name = "Korea Forever !!!";

            Console.WriteLine("Before convert string : {0}", name);
            Console.WriteLine("After convert string : {0}", convertMethod(name));

            Console.ReadLine();
        }

        static string UppercaseString(string inputString)
        {
            return inputString.ToUpper();
        }

 

예제3

 

C#에서는 다음 예제와 같이 Func<T, TResult> 델리게이트를 anonymous method와 함께 사용할 수도 있습니다.

        static void Main(string[] args)
        {
            Func<string, string> convert = delegate(string s)
            {
                return s.ToUpper();
            };

            string name = "Korea Forever !!!";

            Console.WriteLine("Before convert string : {0}", name);
            Console.WriteLine("After convert string : {0}", convert(name));

            Console.ReadLine();
        }

 

예제4

 

다음 예제와 같이 Func<T, TResult> 델리게이트에 람다식을 할당할 수도 있습니다.

            Func<string, string> convert = (string s) => s.ToUpper();

            string name = "Korea Forever !!!";

            Console.WriteLine("Before convert string : {0}", name);
            Console.WriteLine("After convert string : {0}", convert(name));

            Console.ReadLine();

 

4개 예제에 대한 동일한 결과화면 입니다.

 

 

 

'C#' 카테고리의 다른 글

람다식(Lambda Expression) - 4  (0) 2015.06.11
람다식(Lambda Expression) - 3  (0) 2015.06.09
람다식(Lambda Expression) - 1  (0) 2015.06.08
EventHandler 와 EventHandler<TEventArgs> Delegate  (0) 2015.06.04
Event 와 Delegate  (0) 2015.06.01
posted by 따시쿵
2015. 6. 8. 17:22 C#

람다식을 사용하는 이유와 목적을 먼저 살펴 보는게 순서일 듯 싶네요.

 

  • 익명 메소드를 만드는 경우
  • Func와 Action으로 더 간편하게 무명 함수 만드는 경우
  • LINQ를 이용한 방법으로 표현하는 경우
1. 익명 메소드(Anonymous method)를 만드는 경우

 

익명 메소드를 만드는 경우에는 delegate를 이용한 방법도 있습니다. 그런데 람다식을 이용한 방법도 제공하는 이유는 개발 히스토리에 해답이 있습니다.

 

마이크로소프트는 C# 2.0 에서 델리게이트를 이용한 익명 메소드를 만드는 방법을 제공했고, 람다식은 C# 3.0 에서 와서야 도입 되었습니다. 따라서 C# 2.0 에서 작성한 프로그램을 C# 3.0 에서 기능을 삭제할 수는 없는 일입니다.

 

식 형식

 

매개 변수 목록 => 식

 

문 형식

 

(매개 변수 목록) => {

                             ..........................

                             ..........................

                             ..........................

                           };

 

 

 

예제1 : 식 형식의 람다식 표현 

        delegate int Calculate(int a, int b);

        static void Main(string[] args)
        {
            // 1. 람다식을 이용한 표현(매개변수 타입 있는 경우)
            Calculate cal1 = (int a, int b) => a + b;
            Console.WriteLine("Lambda Expression : {0} + {1} = {2}", 4, 5, cal1(4, 5));

            // 2. 람다식을 이용한 표현(매개변수 타입 없는 경우)
            Calculate cal2 = (a, b) => a + b;
            Console.WriteLine("Lambda Expression : {0} + {1} = {2}", 4, 5, cal2(4, 5));

            // 3. 델리게이트를 이용한 표현
            Calculate cal3 = delegate(int a, int b) { return a + b; };
            Console.WriteLine("Delegate : {0} + {1} = {2}", 4, 5, cal3(4,5));

            Console.ReadLine();
        }

 

 

 

 

예제 2: 문 형식의 람다식

 

식 형식의 람다식에서는 반환 형식이 없는 무명 함수를 만들 수 없지만, 문 형식의 람다식을 이용하면 가능합니다.

        delegate void DoSomething();
        static void Main(string[] args)
        {
            DoSomething DoIt = () =>
            {
                Console.WriteLine("여기는 ");
                Console.WriteLine("대한민국 ");
                Console.WriteLine("입니다");
            };

            DoIt();

            Console.ReadLine();
        }

 

 

 

 

 

예제3 : 람다식이 익명 메소드를 대치하는 경우. 파라미터가 생략되는 경우가 있습니다.

 

 

button control 의 Click 이벤트는 EventHandler 델리게이트이며, 두 개의 파라미터를 가집니다. object sender, EventArgs e. 파라미터가 익명 메소드 안에서 사용하지 않는 경우에는 생략하는 경우도 있습니다.

        private void Form1_Load(object sender, EventArgs e)
        {
            Button button1 = new Button();
            button1.Text = "Click me !!!";

            // 1. 델리게이트 이용 & 파라미터를 생략한 경우
            button1.Click += delegate
            {
                MessageBox.Show("Button1 Clicked!");
            };

            // 2. 델리게이트 이용 & 파라미터가 있는 경우
            button1.Click += delegate(object eventSender, EventArgs eventArgs)
            {
                MessageBox.Show("Button1 Clicked!");
            };

            // 3. 람다식을 이용한 식으로 표현함
            button1.Click += (object eventSender, EventArgs eventArgs) => MessageBox.Show("Button1 Clicked!");

            // 4. 람다식을 이용한 문으로 표현함
            button1.Click += (object eventSender, EventArgs eventArgs) => { MessageBox.Show("Button1 Clicked!"); };

            // 5. 람다식 이용 & 파라미터 타입 생략하는 경우
            button1.Click += (eventSender, eventArgs) => { MessageBox.Show("Button1 Clicked!"); };

            this.Controls.Add(button1);
        }

 

 

 

 

 

'C#' 카테고리의 다른 글

람다식(Lambda Expression) - 3  (0) 2015.06.09
람다식(Lambda Expression) - 2  (0) 2015.06.09
EventHandler 와 EventHandler<TEventArgs> Delegate  (0) 2015.06.04
Event 와 Delegate  (0) 2015.06.01
초단위 시간 경과 보이기  (0) 2015.05.12
posted by 따시쿵
2015. 6. 4. 12:10 C#

프로그램 설명


EventHandler 와 EventHandler<TEventArgs> Delegate 를 비교하여 설명하는 예제입니다.

  • EventHandler Delegate  - 이벤트 데이터가 없는 이벤트를 처리할 메서드를 나타냅니다.
  • EventHandler<TEventArgs> Delegate - 이벤트가 데이터를 제공할 때 이벤트를 처리할 메서드를 나타냅니다.

1. EventHandler delegate


구문


[SerializableAttribute]

[ComVisibleAttribute(true)]

public delegate void EventHandler(

Object sender,

EventArgs e

)



매개 변수


sender

형식: System.Object

이벤트 소스입니다. 


e

형식: System.EventArgs

이벤트 데이터가 포함되지 않은 개체입니다. 


설명


.NET Framework의 이벤트 모델은 이벤트와 이벤트 처리기를 연결하는 이벤트 대리자가 있다는 사실을 기초로 만들어졌습니다. 이벤트를 발생시키려면 다음과 같은 두 가지 요소가 필요합니다.


  • 이벤트에 응답하는 메서드를 식별하는 대리자입니다.
  • 이벤트 데이터를 제공 하는 경우 이벤트 데이터를 보유하 고 필요한 경우는 클래스입니다.


대리자는 시그니처를 정의하는 형식, 즉 메서드에 대한 반환 값 형식과 매개 변수 목록 형식입니다. 대리자 형식을 사용하여 대리자와 같은 시그니처가 있는 모든 메서드를 참조하는 변수를 선언할 수 있습니다.


이벤트 처리기 대리자의 표준 시그니처에서 값을 반환 하지 않는 메서드를 정의 합니다. 


첫 번째 매개 변수 형식이 Object 이벤트를 발생 시키는 인스턴스를 참조 합니다. 

두 번째 매개 변수 형식에서 파생 된 EventArgs 및 이벤트 데이터를 보유 합니다. 이벤트에서 이벤트 데이터를 생성 하지 않습니다 두 번째 매개 변수는 단순히 값의 경우는 EventArgs.Empty 필드입니다. 그렇지 않으면 두 번째 매개 변수에서 파생 된 형식입니다 EventArgs 하 고 모든 필드 또는 속성은 이벤트 데이터를 보관 하는 데 필요한을 제공 합니다.


EventHandler 대리자는 특별히 데이터를 생성 하지 하는 이벤트에 대한 이벤트 처리기 메서드를 나타내는 미리 정의된 대리자입니다. 


이벤트를 처리할 메서드와 이벤트를 연결하려면 대리자의 인스턴스를 해당 이벤트에 추가합니다. 대리자를 제거하지 않는 경우, 이벤트가 발생할 때마다 이벤트 처리기가 호출됩니다.


예제

    class Program
    {
        #region Event Publisher
        class Publisher
        {
            public event EventHandler Somethinghappened;

            public void DoSometing()
            {
                EventHandler hanlder = Somethinghappened;
                if (hanlder != null)
                    hanlder(this, EventArgs.Empty);
            }
        }
        #endregion

        #region event Subscriber
        class Subscriber
        {
            public void HanldeEvent(object sender, EventArgs e)
            {
                Console.WriteLine("Something happened to " + sender.ToString());
                Console.ReadLine();
            }
        }
        #endregion

        static void Main(string[] args)
        {
            Publisher publisher = new Publisher();
            Subscriber subscriber = new Subscriber();
            publisher.Somethinghappened += subscriber.HanldeEvent;

            publisher.DoSometing();
        }
    }

소스 파일 : 

MyEventHandler2.zip



2. EventHandler<TEventArgs> delegate


구문


[SerializableAttribute]

public delegate void EventHandler<TEventArgs>(

Object sender,

TEventArgs e

)



형식 매개 변수

TEventArgs

이벤트에서 생성한 이벤트 데이터의 형식입니다.


매개 변수

sender

형식: System.Object

이벤트 소스입니다.

e

형식: TEventArgs

이벤트 데이터를 포함하는 개체입니다. 



설명


.NET Framework의 이벤트 모델은 이벤트와 이벤트 처리기를 연결하는 이벤트 대리자가 있다는 사실을 기초로 만들어졌습니다. 이벤트를 발생시키려면 다음과 같은 두 가지 요소가 필요합니다.


  • 이벤트에 응답하는 메서드를 참조하는 대리자입니다.
  • 이벤트 데이터를 제공 하는 경우 이벤트 데이터를 보유하고 필요한 경우는 클래스입니다.


대리자는 시그니처를 정의하는 형식, 즉 메서드에 대한 반환 값 형식과 매개 변수 목록 형식입니다. 대리자 형식을 사용하여 대리자와 같은 시그니처가 있는 모든 메서드를 참조하는 변수를 선언할 수 있습니다.


이벤트 처리기 대리자의 표준 시그니처에서 값을 반환 하지 않는 메서드를 정의 합니다. 


첫 번째 매개 변수 형식이 Object 이벤트를 발생 시키는 인스턴스를 참조 합니다. 

두 번째 매개 변수 형식에서 파생 된 EventArgs 및 이벤트 데이터를 보유 합니다. 

이벤트에서 이벤트 데이터를 생성 하지 않습니다 두 번째 매개 변수는 단순히 값의 경우는 EventArgs.Empty 필드입니다. 그렇지 않으면 두 번째 매개 변수에서 파생 된 형식입니다 EventArgs 하 고 모든 필드 또는 속성은 이벤트 데이터를 보관 하는 데 필요한을 제공 합니다.


EventHandler<TEventArgs> 대리자는 미리 정의 된 데이터를 생성 하는 이벤트에 대한 이벤트 처리기 메서드를 나타내는 대리자입니다. 


이벤트에서 이벤트 데이터를 생성할 때 EventHandler<TEventArgs>을 사용하면 사용자 지정 대리자를 직접 코딩할 필요가 없습니다. 단순히 이벤트 데이터 개체의 형식을 제네릭 매개 변수로 제공합니다.


이벤트를 처리할 메서드와 이벤트를 연결하려면 대리자의 인스턴스를 해당 이벤트에 추가합니다. 대리자를 제거하지 않는 경우, 이벤트가 발생할 때마다 이벤트 처리기가 호출됩니다.




예제

    class Program
    {
        static void Main(string[] args)
        {
            Counter c = new Counter(new Random().Next(10));
            c.ThresholdReached += c_ThresholdReached;

            Console.WriteLine("press 'a' key to increase total");
            while (Console.ReadKey(true).KeyChar == 'a')
            {
                Console.WriteLine("adding one");
                c.Add(1);
            }            
        }

        static void c_ThresholdReached(object sender, ThresholdReachedEventArgs e)
        {
            Console.WriteLine("The threshold of {0} was reached at {1}.", e.Threshold, e.TimeReached);
            Console.ReadLine();
        }
    }

    class Counter
    {
        private int threshold;
        private int total;

        public Counter(int passedThreshold)
        {
            threshold = passedThreshold;
        }

        public void Add(int x)
        {
            total += x;
            if (total >= threshold)
            {
                ThresholdReachedEventArgs args = new ThresholdReachedEventArgs();
                args.Threshold = threshold;
                args.TimeReached = DateTime.Now;
                OnThresholdReached(args);
            }
        }

        protected virtual void OnThresholdReached(ThresholdReachedEventArgs e)
        {
            EventHandler<ThresholdReachedEventArgs> handler = ThresholdReached;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        public event EventHandler<ThresholdReachedEventArgs> ThresholdReached;
    }

    public class ThresholdReachedEventArgs : EventArgs
    {
        public int Threshold { get; set; }
        public DateTime TimeReached { get; set; }
    }


소스 파일 :


MyEventHandler1.zip






'C#' 카테고리의 다른 글

람다식(Lambda Expression) - 2  (0) 2015.06.09
람다식(Lambda Expression) - 1  (0) 2015.06.08
Event 와 Delegate  (0) 2015.06.01
초단위 시간 경과 보이기  (0) 2015.05.12
로그인 창  (0) 2015.05.08
posted by 따시쿵