성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Java - How to use the Foreign Funct...
[정성태] 제가 큰 실수를 했군요. ^^; Delegate를 통한 Bein...
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>C# - foreach에서 열거 변수의 타입을 var로 쓰면 object로 추론하는 문제</h1> <p> 아래와 같은 질문이 있는데요,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > var를 사용할 수 없는 이유가 궁금합니다! ; <a target='tab' href='https://www.sysnet.pe.kr/3/0/5452'>https://www.sysnet.pe.kr/3/0/5452</a> </pre> <br /> 실제로 위의 글에 실린 예제에서 foreach 열거 변수의 타입을 var로 바꾸면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System; using System.Collections; class Book { public long ISBN { get; set; } public string Writer { get; set; } = string.Empty; public string PublishingCompany { get; set; } = string.Empty; } class Bookcase : <span style='color: blue; font-weight: bold'>IEnumerable</span> { ArrayList _books = new ArrayList(); public void Add(Book book) { _books.Add(book); } <span style='color: blue; font-weight: bold'>public IEnumerator GetEnumerator() { return _books.GetEnumerator(); }</span> } class Program { static void Main(string[] args) { var bookcase = new Bookcase(); bookcase.Add(new Book() { ISBN = 9791158391805, Writer = "JungSeongtae", PublishingCompany = "wikibooks", }); foreach (<span style='color: blue; font-weight: bold'>var</span> item in bookcase) { Console.WriteLine(<span style='color: blue; font-weight: bold'>item.ISBN</span>); // 컴파일 오류: Error CS1061 'object' does not contain a definition for 'ISBN' and no accessible extension method 'ISBN' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?) } } } </pre> <br /> 해당 변수의 ISBN 필드를 접근하는 코드에서 컴파일 오류가 발생합니다. 즉, 위의 코드는 사실상 다음과 같이 인식되므로,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > foreach (<span style='color: blue; font-weight: bold'>object</span> item in bookcase) { Console.WriteLine(item.ISBN); } </pre> <br /> 당연히 object 타입의 ISBN 필드는 존재하지 않아 컴파일 오류가 발생하는 것입니다. 그리고, C# 컴파일러가 그런 식으로 추론할 수밖에 없는 이유는 IEnumerable 인터페이스로부터 어떠한 타입 정보도 얻을 수 없기 때문입니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 따라서, 해결 방법은 타입 정보를 알 수 있는 IEnumerable<T> 인터페이스를 구현하면 되는 것입니다. 가령, 위의 Bookcase 타입은 다음과 같은 식으로 코드를 추가할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class Bookcase : <span style='color: blue; font-weight: bold'>IEnumerable<Book></span> { List<Book> _books = new List<Book>(); public void Add(Book book) { _books.Add(book); } public IEnumerator GetEnumerator() { return _books.GetEnumerator(); } <span style='color: blue; font-weight: bold'>public IEnumerator<Book> GetEnumerator()</span> { return _books.GetEnumerator(); } } </pre> <br /> 그런데, 저렇게 구현하면 동일한 이름의 GetEnumerator 메서드를 2개 정의한 것이므로 컴파일 오류가 발생합니다. 따라서, 최종적으로는 다음과 같이 IEnumerable의 GetEnumerator 버전을 숨기도록 "<a target='tab' href='https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/interfaces/explicit-interface-implementation'>명시적인 인터페이스 구현</a>" 방식을 사용해야 합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > class Bookcase : IEnumerable<Book> { List<Book> _books = new List<Book>(); public void Add(Book book) { _books.Add(book); } // 명시적인 인터페이스 구현으로 타입에서 IEnumerable 버전의 GetEnumerator() 메서드를 숨김 처리 <span style='color: blue; font-weight: bold'>IEnumerator IEnumerable.GetEnumerator()</span> { return _books.GetEnumerator(); } // 따라서 C# 컴파일러는 IEnumerable<Book> 버전을 선택하므로 Book 타입 정보를 구함 IEnumerator<Book> IEnumerable<Book>.GetEnumerator() { return _books.GetEnumerator(); } } </pre> <br /> 이후 다시 빌드하면 정상적으로 "var item"이 "Book item"으로 인식되는 것을 확인할 수 있습니다.<br /> <br /> (<a target='tab' href='https://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=1710&boardid=331301885'>첨부 파일은 이 글의 예제 코드를 포함</a>합니다.)<br /> <br /> <hr style='width: 50%' /><br /> <br /> 참고로, 만약 대상 타입이 소스 코드를 변경할 수 없는 경우라면 어떻게 해야 할까요?<br /> <br /> 어쩔 수 없습니다. 이런 경우는 확장 메서드를 사용해 IEnumerable<T>를 반환하는 별도의 메서드를 정의해 그걸 사용해야 합니다. 그리고 그런 확장 메서드는 이미 만들어진 것이 있으므로 그냥 다음과 같이 사용할 수 있습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > using System.Linq; // <a target='tab' href='https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.cast'>Cast</a>가 Linq의 확장 메서드이므로. foreach (var item in bookcase.<span style='color: blue; font-weight: bold'>Cast<Book>()</span>) { Console.WriteLine(item.ISBN); } </pre> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
4158
(왼쪽의 숫자를 입력해야 합니다.)