etc./StackOverFlow

목록에서 상속하지 않는 이유<T> ?

청렴결백한 만능 재주꾼 2022. 2. 8. 08:37
반응형

질문자 :Superbest


프로그램을 계획할 때 나는 종종 다음과 같은 일련의 생각으로 시작합니다.

축구팀은 축구 선수들의 명단일 뿐입니다. 따라서 다음과 같이 표현해야 합니다.

 var football_team = new List<FootballPlayer>();

이 목록의 순서는 선수가 명단에 나열된 순서를 나타냅니다.

그러나 나중에 팀에 단순한 선수 목록 외에 기록해야 하는 다른 속성도 있다는 것을 알게 되었습니다. 예를 들어 이번 시즌의 누적 득점, 현재 예산, 유니폼 색상, 팀 이름을 나타내는 string

그래서 저는 다음과 같이 생각합니다.

좋아, 축구 팀은 선수 목록과 같지만 추가로 이름( string )과 누적 점수( int )가 있습니다. .NET은 미식축구팀을 저장하는 클래스를 제공하지 않으므로 나만의 클래스를 만들어 보겠습니다. 가장 유사하고 관련성이 높은 기존 구조는 List<FootballPlayer> 이므로 상속합니다.

 class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }

그러나 List<T> 에서 상속해서는 안 된다고 나와 있습니다 . 나는 이 지침에 대해 두 가지 측면에서 완전히 혼란스럽습니다.

왜 안 돼?

분명히 List 는 성능에 최적화되어 있습니다. 어때요? List 확장하면 어떤 성능 문제가 발생합니까? 정확히 무엇을 깰 것인가?

내가 본 또 다른 이유는 List 이 Microsoft에서 제공하고 내가 제어 할 수 없기 때문에 "공개 API"를 노출한 후 나중에 변경할 수 없다는 것 입니다. 그러나 나는 이것을 이해하기 위해 고군분투합니다. 공개 API란 무엇이며 왜 주의해야 합니까? 현재 프로젝트에 이 공개 API가 없거나 없을 가능성이 있는 경우 이 지침을 안전하게 무시할 수 있습니까? 내가 상속 경우 List 내가 공용 API를 필요로 밝혀, 어떤 어려움이 나는 것인가?

왜 그것이 중요합니까? 목록은 목록입니다. 무엇이 바뀔 수 있습니까? 내가 무엇을 바꾸고 싶습니까?

마지막으로 Microsoft가 내가 List 에서 상속받는 것을 원하지 않았다면 왜 클래스를 sealed 하지 않았을까요?

또 무엇을 사용해야 합니까?

분명히 사용자 지정 컬렉션의 경우 Microsoft는 List 대신 확장되어야 Collection 클래스를 제공했습니다. 그러나 이 클래스는 매우 AddRange 하고 예를 들어 AddRange와 같은 유용한 기능이 많지 않습니다. jvitor83의 대답은 특정 방법에 대한 성능의 이론적 근거를 제공하지만, 어떻게 느린 AddRange 없는 것보다 낫지 AddRange ?

Collection 에서 List 에서 상속하는 것보다 훨씬 더 많은 작업이며 이점이 없습니다. 확실히 Microsoft는 아무 이유 없이 추가 작업을 하라고 말하지 않을 것입니다. 그래서 저는 제가 뭔가를 오해하고 있는 것 같은 느낌을 지울 수 없으며 Collection 상속하는 것은 실제로 제 문제에 대한 올바른 해결책이 아닙니다.

IList 구현과 같은 제안을 보았습니다. 그냥 싫다. 이것은 나에게 아무 것도 얻지 못하는 수십 줄의 상용구 코드입니다.

마지막으로 어떤 사람들은 List 을 다음과 같이 래핑할 것을 제안합니다.

 class FootballTeam { public List<FootballPlayer> Players; }

여기에는 두 가지 문제가 있습니다.

  1. 그것은 내 코드를 불필요하게 장황하게 만듭니다. 지금 호출해야 my_team.Players.Count 대신의 my_team.Count . 고맙게도 C#을 사용하면 인덱서를 정의하여 인덱싱을 투명하게 만들고 내부 List 의 모든 메서드를 전달할 수 있습니다. 하지만 코드가 너무 많습니다! 나는 그 모든 일에 대해 무엇을 얻습니까?

  2. 그냥 아무 의미가 없습니다. 축구팀은 선수 목록을 "가지고 있지" 않습니다. 그것은 플레이어의 목록입니다. "John McFootballer가 SomeTeam의 선수에 합류했습니다"라고 말하지 않습니다. "John이 SomeTeam에 합류했습니다"라고 말합니다. "문자열의 문자"에 문자를 추가하지 않고 문자열에 문자를 추가합니다. 도서관의 책에 책을 추가하는 것이 아니라 책을 도서관에 추가하는 것입니다.

"후드에서" 일어나는 일은 "Y의 내부 목록에 X를 추가하는 것"이라고 말할 수 있지만, 이것은 세상에 대해 매우 반직관적인 사고 방식처럼 보입니다.

내 질문(요약)

"논리적으로"(즉, "인간의 마음에") 몇 가지 종소리가 list things

List<T> 에서 상속하는 것은 항상 허용되지 않습니까? 언제 허용됩니까? 왜 왜 안돼? List<T> 에서 상속할지 여부를 결정할 때 프로그래머는 무엇을 고려해야 합니까?



여기에 몇 가지 좋은 답변이 있습니다. 나는 그들에게 다음 사항을 추가할 것입니다.

"논리적으로"(즉, "인간의 마음에") 몇 가지 종소리가 있는 목록일 뿐인 데이터 구조를 나타내는 올바른 C# 방식은 무엇입니까?

축구의 존재에 대해 잘 알고 있는 컴퓨터 프로그래머가 아닌 10명에게 빈칸을 채워달라고 요청하십시오.

축구팀은 특정한 종류의 _____

누가 "축구 선수 명단"이라고 말했습니까? 아니면 모두 "스포츠 팀"이나 "클럽" 또는 "조직"이라고 말했습니까? 축구팀이 특정한 종류의 선수 목록이라는 당신의 생각은 인간의 마음과 인간의 마음에만 있습니다.

List<T>메커니즘 입니다. 축구 팀은 비즈니스 개체 입니다. 즉, 프로그램 의 비즈니스 도메인 에 있는 일부 개념을 나타내는 개체입니다. 그것들을 섞지 마십시오! 축구팀 은 일종의 팀입니다. 그것은 명단을 가지고 있고 , 명단은 선수들의 목록입니다 . 명단은 특정한 종류의 선수 목록 이 아닙니다. 로스터 선수의 목록입니다. List<Player> Roster 라는 속성을 만드십시오. 축구팀에 대해 알고 있는 모든 사람이 명단에서 선수를 삭제할 수 있다고 생각하지 않는 한 ReadOnlyList<Player>

List<T> 에서 상속하는 것은 항상 허용되지 않습니까?

누구에게 용납되지 않는가? 나? 아니요.

언제 허용됩니까?

List<T> 메커니즘을 확장 하는 메커니즘을 구축할 때.

List<T> 에서 상속할지 여부를 결정할 때 프로그래머는 무엇을 고려해야 합니까?

나는 메커니즘 이나 비즈니스 개체를 구축하고 있습니까?

그러나 그것은 많은 코드입니다! 나는 그 모든 일에 대해 무엇을 얻습니까?

List<T> 의 관련 멤버에 대한 전달 방법을 작성하는 데 50번 넘게 걸렸을 질문을 입력하는 데 더 많은 시간을 할애했습니다. 당신은 장황한 것을 두려워하지 않습니다. 그리고 우리는 여기서 아주 적은 양의 코드에 대해 이야기하고 있습니다. 이것은 몇 분의 작업입니다.

업데이트

나는 그것에 대해 좀 더 생각했고 축구 팀을 선수 목록으로 모델링하지 않는 또 다른 이유가 있습니다. 사실 축구팀을 선수 목록 도 가지고 있는 것으로 모델링하는 것은 나쁜 생각일 수 있습니다. 선수 목록을 가지고 있는 팀의 문제는 여러분이 가지고 있는 것이 특정 시점 의 팀 스냅샷 이라는 것입니다. 이 수업에 대한 귀하의 비즈니스 사례가 무엇인지 모르지만 축구 팀을 대표하는 수업이 있다면 "2003년에서 2013년 사이에 부상으로 인해 몇 명의 Seahawks 선수가 경기를 결장했습니까?"와 같은 질문을 하고 싶습니다. 또는 "이전에 다른 팀에서 뛰었던 덴버 선수 중 야드가 전년 대비 가장 많이 증가한 선수는?" 또는 " Piggers는 올해 끝까지 갔습니까? "

즉, 축구 팀은 선수가 언제 영입, 부상, 은퇴했는지 등과 같은 역사적 사실의 모음 으로 잘 모델링된 것 같습니다. 분명히 현재 선수 명단은 아마도 전면에 나와야 하는 중요한 사실입니다. 하지만 더 역사적인 관점이 필요한 이 개체로 하고 싶은 다른 흥미로운 일이 있을 수 있습니다.


Eric Lippert

와우, 귀하의 게시물에는 수많은 질문과 요점이 있습니다. Microsoft에서 얻을 수 있는 대부분의 추론은 정확히 일치합니다. List<T> 대한 모든 것부터 시작하겠습니다.

  • List<T> 고도로 최적화되어 있습니다. 주요 용도는 개체의 개인 구성원으로 사용하는 것입니다.
  • Microsoft는 때때로 더 친숙한 이름을 가진 클래스를 만들고 싶을 수도 있기 때문에 이를 봉인하지 않았습니다. class MyList<T, TX> : List<CustomObject<T, Something<TX>> { ... } . 이제 var list = new MyList<int, string>(); .
  • CA1002: 일반 목록을 노출하지 마십시오 . 기본적으로 이 앱을 단독 개발자로 사용할 계획이더라도 좋은 코딩 방법으로 개발하는 것이 좋습니다. 소비자가 인덱싱된 목록을 가져야 하는 경우 IList<T> 로 노출할 수 있습니다. 이렇게 하면 나중에 클래스 내에서 구현을 변경할 수 있습니다.
  • Collection<T> 매우 일반적인 개념으로 만들었습니다. 이름이 모든 것을 말해줍니다. 그것은 단지 컬렉션입니다. SortedCollection<T> , ObservableCollection<T> , ReadOnlyCollection<T> 등과 같은 보다 정확한 버전이 있습니다. 각각 IList<T> 구현하지만 List<T> 구현하지 않습니다 .
  • Collection<T> 사용하면 구성원(예: 추가, 제거 등)이 가상이므로 재정의할 수 있습니다. List<T> 는 그렇지 않습니다.
  • 귀하의 질문의 마지막 부분은 제자리에 있습니다. 축구 팀은 단순한 선수 목록이 아니므로 해당 선수 목록을 포함하는 클래스여야 합니다. 구성 대 상속을 생각하십시오. 축구 팀 에는 선수 목록(명단)이 있지만 선수 목록이 아닙니다.

내가 이 코드를 작성했다면 클래스는 다음과 같을 것입니다.

 public class FootballTeam { // Football team rosters are generally 53 total players. private readonly List<T> _roster = new List<T>(53); public IList<T> Roster { get { return _roster; } } // Yes. I used LINQ here. This is so I don't have to worry about // _roster.Length vs _roster.Count vs anything else. public int PlayerCount { get { return _roster.Count(); } } // Any additional members you want to expose/wrap. }

myermian

class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal; }

이전 코드는 거리에서 축구를 하고 있는 많은 사람들을 의미합니다. 그리고 그들은 우연히 이름을 갖게 되었습니다. 다음과 같은 것:

축구하는 사람들

어쨌든이 코드 (내 답변에서)

 public class FootballTeam { // A team's name public string TeamName; // Football team rosters are generally 53 total players. private readonly List<T> _roster = new List<T>(53); public IList<T> Roster { get { return _roster; } } public int PlayerCount { get { return _roster.Count(); } } // Any additional members you want to expose/wrap. }

의미: 이것은 관리, 선수, 관리자 등이 있는 축구 팀입니다. 다음과 같습니다.

Manchester United 팀의 구성원(및 기타 속성)을 보여주는 이미지

이것이 당신의 논리가 그림으로 표현되는 방식입니다 ...


Nean Der Thal

이것은 구성상속 의 고전적인 예입니다.

이 특정한 경우:

행동이 추가된 플레이어의 팀 목록입니다.

또는

플레이어 목록을 포함하는 팀 자체 개체입니다.

List를 확장하면 여러 가지 방법으로 자신을 제한할 수 있습니다.

  1. 액세스를 제한할 수 없습니다(예: 명단 변경 중지). 모두 필요/원하지 않는 모든 List 메서드를 얻을 수 있습니다.

  2. 다른 것들의 목록도 갖고 싶다면 어떻게 될까요? 예를 들어, 팀에는 코치, 관리자, 팬, 장비 등이 있습니다. 그 중 일부는 자체 목록에 있을 수 있습니다.

  3. 상속 옵션을 제한합니다. 예를 들어 일반 Team 개체를 만든 다음 이 개체에서 상속하는 BaseballTeam, FootballTeam 등이 있을 수 있습니다. List에서 상속을 받으려면 Team에서 상속을 수행해야 하지만 이는 모든 다양한 유형의 팀이 해당 명단을 동일하게 구현해야 함을 의미합니다.

구성 - 개체 내에서 원하는 동작을 제공하는 개체를 포함합니다.

상속 - 개체는 원하는 동작이 있는 개체의 인스턴스가 됩니다.

둘 다 용도가 있지만 이것은 구성이 선호되는 분명한 경우입니다.


Tim B

모두가 지적했듯이 플레이어 팀은 플레이어 목록이 아닙니다. 이 실수는 아마도 다양한 수준의 전문 지식을 가진 많은 사람들이 범하는 것입니다. 종종 문제는 이 경우와 같이 미묘하고 때로는 매우 총체적입니다. 이러한 설계는 Liskov 대체 원리를 위반하기 때문에 좋지 않습니다. 인터넷에는 이 개념을 설명하는 좋은 기사가 많이 있습니다. 예: http://en.wikipedia.org/wiki/Liskov_substitution_principle


Community Wiki

FootballTeam 에 메인 팀과 함께 예비 팀이 있으면 어떻게 되나요?

 class FootballTeam { List<FootballPlayer> Players { get; set; } List<FootballPlayer> ReservePlayers { get; set; } }

그것을 어떻게 모델링 하시겠습니까?

 class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }

관계는 명확 인을 가지고하지 않는 것입니다.

또는 RetiredPlayers ?

 class FootballTeam { List<FootballPlayer> Players { get; set; } List<FootballPlayer> ReservePlayers { get; set; } List<FootballPlayer> RetiredPlayers { get; set; } }

일반적으로 컬렉션에서 상속하려면 클래스 이름을 SomethingCollection 지정합니다.

당신의 SomethingCollection 의미상 의미가 있습니까? 유형이 Something 의 컬렉션인 경우에만 이 작업을 수행합니다.

FootballTeam 의 경우 제대로 들리지 않습니다. Team Collection 그 이상입니다. Team 에는 코치, 트레이너 등이 있을 수 있습니다.

FootballCollection 은 축구공 모음 또는 축구 도구 모음처럼 들립니다. TeamCollection , 팀의 컬렉션입니다.

FootballPlayerCollection 은 정말로 원하는 경우 List<FootballPlayer> 에서 상속되는 클래스의 유효한 이름이 될 플레이어 모음처럼 들립니다.

정말로 List<FootballPlayer> 는 다루기에 완벽하게 좋은 유형입니다. 메서드에서 반환하는 경우 IList<FootballPlayer>

요약해서 말하자면

자신에게 물어

  1. X Y ? 또는 가지고 X Y ?

  2. 내 클래스 이름은 무엇을 의미합니까?


Sam Leach

디자인 > 구현

노출하는 메서드와 속성은 디자인 결정입니다. 상속받은 기본 클래스는 구현 세부 사항입니다. 나는 전자로 한 발짝 물러설 가치가 있다고 생각합니다.

객체는 데이터와 행동의 모음입니다.

따라서 첫 번째 질문은 다음과 같아야 합니다.

  • 내가 만들고 있는 모델에서 이 개체는 어떤 데이터를 구성합니까?
  • 이 개체는 해당 모델에서 어떤 동작을 나타냅니까?
  • 이것은 미래에 어떻게 바뀔 수 있습니까?

상속은 "isa"(a) 관계를 의미하는 반면 구성은 "hasa" 관계를 의미한다는 점을 명심하십시오. 애플리케이션이 발전함에 따라 상황이 달라질 수 있음을 염두에 두고 상황에 맞는 것을 선택하십시오.

구체적인 유형으로 생각하기 전에 인터페이스에서 생각하는 것을 고려하십시오. 어떤 사람들은 그런 식으로 "디자인 모드"에 두뇌를 두는 것이 더 쉽다고 생각합니다.

이것은 모든 사람이 일상적인 코딩에서 이 수준에서 의식적으로 하는 것이 아닙니다. 그러나 이런 종류의 주제를 숙고하고 있다면 당신은 디자인의 영역을 밟고 있는 것입니다. 그것을 알아차림으로써 해방될 수 있다.

디자인 사양 고려

MSDN 또는 Visual Studio List<T>IList<T> 를 살펴보십시오. 그들이 노출하는 메소드와 속성을 확인하십시오. 이 모든 방법이 누군가가 FootballTeam 에 하고 싶어하는 것처럼 보입니까?

footballTeam.Reverse() 가 이해가 됩니까? footballTeam.ConvertAll<TOutput>() 이 원하는 것처럼 보이나요?

이것은 속임수 질문이 아닙니다. 대답은 진정으로 "예"일 수 있습니다. List<Player> 또는 IList<Player> 를 구현/상속하면 계속 사용됩니다. 그것이 당신의 모델에 이상적이라면 그렇게 하십시오.

예라고 결정했다면 이는 의미가 있으며 개체를 플레이어(동작)의 컬렉션/목록으로 처리할 수 있기를 원하므로 ICollection<Player> 또는 IList<Player> 를 구현하려는 경우 반드시 그렇게 하십시오. 개념적으로:

 class FootballTeam : ... ICollection<Player> { ... }

개체에 플레이어(데이터)의 컬렉션/목록이 포함되도록 하고 따라서 컬렉션이나 목록이 속성 또는 구성원이 되기를 원하는 경우 반드시 그렇게 하십시오. 개념적으로:

 class FootballTeam ... { public ICollection<Player> Players { get { ... } } }

사람들이 플레이어 집합을 세거나 추가하거나 제거하지 않고 열거만 할 수 있기를 원할 수 있습니다. IEnumerable<Player> 는 고려해야 할 완벽하게 유효한 옵션입니다.

이러한 인터페이스 중 어느 것도 모델에서 전혀 유용하지 않다고 느낄 수 있습니다. 이것은 가능성이 적습니다( IEnumerable<T> 는 많은 상황에서 유용합니다). 그러나 여전히 가능합니다.

이 중 하나가 모든 경우에 명백하고 결정적으로 틀렸다고 말하려고 하는 사람은 잘못된 것입니다. 당신에게 그것이 모든 경우에 명백하고 결정적으로 옳다고 말하려고 하는 사람은 잘못 인도된 것입니다.

구현으로 이동

데이터와 동작을 결정했으면 구현에 대한 결정을 내릴 수 있습니다. 여기에는 상속 또는 구성을 통해 의존하는 구체적인 클래스가 포함됩니다.

이것은 큰 단계가 아닐 수 있으며, 사람들은 종종 디자인과 구현을 혼동합니다. 왜냐하면 1-2초 안에 모든 것을 머릿속에서 실행하고 타이핑을 시작할 수 있기 때문입니다.

사고 실험

인위적인 예: 다른 사람들이 언급했듯이 팀이 항상 "단순한" 선수들의 집합은 아닙니다. 팀의 경기 점수 모음을 유지 관리합니까? 팀은 귀하의 모델에서 클럽과 교체 가능합니까? 만약 그렇다면, 그리고 당신의 팀이 선수들의 모음이라면, 아마도 스태프 모음 및/또는 점수 모음일 것입니다. 그러면 다음과 같이 끝납니다.

 class FootballTeam : ... ICollection<Player>, ICollection<StaffMember>, ICollection<Score> { .... }

디자인에도 불구하고 C#의 이 시점에서는 C#이 단일 상속을 "only" 지원하기 때문에 어쨌든 List<T> (C++에서 이 malarkey를 시도했다면 좋은 일이라고 생각할 수 있습니다.) 상속을 통해 하나의 컬렉션을 구현하고 구성을 통해 하나를 구현하는 것은 더럽게 느껴질 수 있습니다. Count 와 같은 속성은 ILIst<Player>.Count .Count 및 IList<StaffMember>.Count 등을 명시적으로 구현하지 않으면 사용자에게 혼동을 주어 혼란스럽기보다는 고통스러울 뿐입니다. 이것이 어디로 가는지 알 수 있습니다. 이 길을 생각하는 동안 직감이 이 방향으로 향하는 것이 잘못되었다고 말할 수 있습니다.

짧은 답변(너무 늦음)

컬렉션 클래스에서 상속하지 않는 것에 대한 지침은 C#에 국한되지 않으며 많은 프로그래밍 언어에서 찾을 수 있습니다. 율법이 아니라 지혜를 받은 것입니다. 한 가지 이유는 실제로 구성이 이해 가능성, 구현 가능성 및 유지 관리 가능성 측면에서 상속보다 우위에 있는 것으로 간주되기 때문입니다. 특히 시간이 지남에 따라 코드에 있는 개체의 정확한 데이터와 동작이 추상화되지 않는 한 유용하고 일관된 "hasa" 관계보다 유용하고 일관된 "hasa" 관계를 찾는 것이 실제 세계/도메인 개체에서 더 일반적입니다. 변경. 그렇다고 해서 컬렉션 클래스에서 상속하는 것을 항상 배제해서는 안 됩니다. 그러나 암시적일 수 있습니다.


El Zorko

우선 사용성과 관련이 있습니다. 상속을 사용하는 경우 Team 클래스는 순수하게 개체 조작을 위해 설계된 동작(메서드)을 노출합니다. 예를 들어 AsReadOnly() 또는 CopyTo(obj) 메서드는 팀 개체에 대해 의미가 없습니다. AddRange(items) 메서드 대신에 더 설명적인 AddPlayers(players) 메서드가 필요할 수 있습니다.

ICollection<T> 또는 IEnumerable<T> 와 같은 제네릭 인터페이스를 구현하는 것이 더 합리적입니다.

언급했듯이 구성은 이에 대한 올바른 방법입니다. 플레이어 목록을 개인 변수로 구현하기만 하면 됩니다.


Dmitry S.

질문을 다시 작성하겠습니다. 그래서 당신은 다른 관점에서 주제를 볼 수 있습니다.

축구팀을 대표해야 할 때, 기본적으로는 이름이라는 것을 이해합니다. 예: "이글스"

 string team = new string();

그리고 나서 팀에도 선수가 있다는 것을 깨달았습니다.

플레이어 목록도 포함하도록 문자열 유형을 확장할 수 없는 이유는 무엇입니까?

문제에 대한 귀하의 입장은 임의적입니다. 그것이없는 것을, 팀이 (속성)가 무엇을 생각하려고합니다.

그렇게 하면 다른 클래스와 속성을 공유하는지 확인할 수 있습니다. 그리고 상속에 대해 생각해보십시오.


user1852503

컨텍스트에 따라 다릅니다

당신의 팀을 선수들의 목록으로 생각할 때, 당신은 축구팀의 "아이디어"를 한 가지 측면으로 투영하고 있는 것입니다. 당신은 "팀"을 경기장에서 보는 사람들로 축소합니다. 이 투영은 특정 상황에서만 정확합니다. 다른 맥락에서 이것은 완전히 틀릴 수 있습니다. 당신이 팀의 후원자가 되고 싶다고 상상해보세요. 따라서 팀의 관리자와 이야기해야 합니다. 이 컨텍스트에서 팀은 관리자 목록에 투영됩니다. 그리고 이 두 목록은 일반적으로 많이 겹치지 않습니다. 다른 컨텍스트는 현재 대 이전 플레이어 등입니다.

불명확한 의미

따라서 팀을 플레이어 목록으로 간주할 때의 문제는 의미 체계가 컨텍스트에 따라 달라지고 컨텍스트가 변경될 때 확장될 수 없다는 것입니다. 또한 어떤 컨텍스트를 사용하고 있는지 표현하기가 어렵습니다.

클래스는 확장 가능

멤버가 하나만 있는 클래스(예: IList activePlayers )를 사용하는 경우 멤버 이름(및 추가로 해당 주석)을 사용하여 컨텍스트를 명확하게 할 수 있습니다. 추가 컨텍스트가 있는 경우 추가 구성원을 추가하기만 하면 됩니다.

수업이 더 복잡하다

어떤 경우에는 추가 클래스를 만드는 것이 과도할 수 있습니다. 각 클래스 정의는 클래스 로더를 통해 로드되어야 하며 가상 머신에 의해 캐시됩니다. 이로 인해 런타임 성능과 메모리가 소모됩니다. 매우 구체적인 맥락이 있는 경우 축구팀을 선수 목록으로 간주하는 것이 좋습니다. 그러나 이 경우 실제로 IList에서 파생된 클래스가 아니라 IList

결론 / 고려 사항

매우 구체적인 컨텍스트가 있는 경우 팀을 플레이어 목록으로 간주하는 것이 좋습니다. 예를 들어 메소드 내에서 다음과 같이 작성해도 무방합니다.

 IList<Player> footballTeam = ...

F#을 사용할 때 형식 약어를 만드는 것도 괜찮습니다.

 type FootballTeam = IList<Player>

그러나 문맥이 더 광범위하거나 불분명한 경우에는 이렇게 해서는 안 됩니다. 이것은 특히 미래에 사용될 수 있는 컨텍스트가 명확하지 않은 새 클래스를 생성하는 경우입니다. 경고 표시는 클래스에 추가 속성(팀 이름, 코치 등)을 추가하기 시작할 때 나타납니다. 이것은 클래스가 사용될 컨텍스트가 고정되어 있지 않고 미래에 변경될 것이라는 분명한 신호입니다. 이 경우 팀을 선수 목록으로 간주할 수 없지만 (현재 활동 중이거나 부상을 당하지 않은 등) 선수 목록을 팀의 속성으로 모델링해야 합니다.


stefan.schwetschke

축구팀은 축구선수 명단 이 아닙니다. 축구 팀은 축구 선수의 목록으로 구성됩니다!

이것은 논리적으로 잘못되었습니다.

 class FootballTeam : List<FootballPlayer> { public string TeamName; public int RunningTotal }

그리고 이것은 정확합니다:

 class FootballTeam { public List<FootballPlayer> players public string TeamName; public int RunningTotal }

Mauro Sampietro

다른 답변은 축구 팀이 "is-a" List<FootballPlayer> 또는 "has-a" List<FootballPlayer> 라는 접선에서 거의 벗어나지 않는다고 생각하기 때문에 실제로 이 질문에 작성된 대로 답변하지 않습니다.

List<T> 상속에 대한 지침에 대한 설명을 요청합니다.

List<T> 에서 상속해서는 안 됩니다. 왜 안 돼?

List<T> 에는 가상 메서드가 없기 때문입니다. 이것은 일반적으로 비교적 적은 고통으로 구현을 전환할 수 있기 때문에 자신의 코드에서는 문제가 되지 않지만 공개 API에서는 훨씬 더 큰 문제가 될 수 있습니다.

공개 API란 무엇이며 왜 주의해야 합니까?

공개 API는 타사 프로그래머에게 노출되는 인터페이스입니다. 프레임워크 코드를 생각하십시오. 그리고 참조되는 지침은 ".NET 응용 프로그램 설계 지침"이 아니라 ".NET Framework 설계 지침"임을 기억하십시오. 차이점이 있으며 일반적으로 공개 API 디자인은 훨씬 더 엄격합니다.

현재 프로젝트에 이 공개 API가 없거나 없을 가능성이 있는 경우 이 지침을 안전하게 무시할 수 있습니까? List에서 상속했는데 공개 API가 필요한 경우 어떤 어려움이 있습니까?

꽤 많이, 예. 어쨌든 자신의 상황에 적용되는지 확인하기 위해 그 이면의 근거를 고려할 수 있지만 공개 API를 빌드하지 않는 경우 버전 관리(이 중 하나인 하위 집합).

나중에 공개 API를 추가하는 경우 구현에서 API를 추상화하거나( List<T> 직접 노출하지 않음) 수반할 수 있는 미래 고통으로 지침을 위반해야 합니다.

왜 그것이 중요합니까? 목록은 목록입니다. 무엇이 바뀔 수 있습니까? 내가 무엇을 바꾸고 싶습니까?

상황에 따라 다르지만 FootballTeam FootballPlayer 추가하면 팀이 샐러리 캡을 초과하게 될 경우 추가할 수 없다고 상상해 보십시오. 이를 추가하는 가능한 방법은 다음과 같습니다.

 class FootballTeam : List<FootballPlayer> { override void Add(FootballPlayer player) { if (this.Sum(p => p.Salary) + player.Salary > SALARY_CAP)) { throw new InvalidOperationException("Would exceed salary cap!"); } } }

아...하지만 virtual 아니기 때문에 override Add 할 수 없습니다(성능상의 이유로).

응용 프로그램에 있는 경우(기본적으로 사용자와 모든 호출자가 함께 컴파일됨을 의미함) 이제 IList<T> 를 사용하도록 변경하고 컴파일 오류를 수정할 수 있습니다.

 class FootballTeam : IList<FootballPlayer> { private List<FootballPlayer> Players { get; set; } override void Add(FootballPlayer player) { if (this.Players.Sum(p => p.Salary) + player.Salary > SALARY_CAP)) { throw new InvalidOperationException("Would exceed salary cap!"); } } /* boiler plate for rest of IList */ }

그러나 제3자에게 공개적으로 노출된 경우 컴파일 및/또는 런타임 오류를 일으키는 주요 변경을 수행한 것입니다.

TL;DR - 가이드라인은 공개 API를 위한 것입니다. 비공개 API의 경우 원하는 대로 수행합니다.


Mark Brackett

사람들이 말할 수 있도록 허용합니까

 myTeam.subList(3, 5);

전혀 의미가 있습니까? 그렇지 않은 경우 목록이 아니어야 합니다.


Cruncher

여기에는 훌륭한 답변이 많이 있지만 언급하지 않은 것에 대해 언급하고 싶습니다. 객체 지향 디자인은 객체에 권한을 부여하는 것입니다 .

모든 규칙, 추가 작업 및 내부 세부 정보를 적절한 개체 안에 캡슐화하려고 합니다. 이런 식으로 이 개체와 상호 작용하는 다른 개체는 모든 것에 대해 걱정할 필요가 없습니다. 사실, 당신은 한 단계 더 나아가 다른 객체가 이러한 내부를 우회하는 것을 적극적으로 방지하기를 원합니다.

List 에서 상속하면 다른 모든 객체가 사용자를 List로 볼 수 있습니다. 플레이어를 추가 및 제거하는 방법에 직접 액세스할 수 있습니다. 그리고 당신은 통제력을 잃을 것입니다. 예를 들어:

플레이어가 은퇴, 사임 또는 해고되었는지 여부를 확인하여 플레이어가 떠날 때를 구별하려고 한다고 가정해 보겠습니다. 적절한 입력 열거형을 사용 RemovePlayer 메서드를 구현할 수 있습니다. 그러나 List Remove , RemoveAllClear 대한 직접 액세스를 방지할 수 없습니다. 결과적으로 당신은 실제로 FootballTeam 클래스의 권한을 박탈했습니다.


캡슐화에 대한 추가 생각... 다음과 같은 문제를 제기했습니다.

그것은 내 코드를 불필요하게 장황하게 만듭니다. 이제 my_team.Count 대신 my_team.Players.Count를 호출해야 합니다.

모든 클라이언트가 당신의 팀을 사용하기 위해 불필요하게 장황할 것입니다. List Players 를 모든 사람에게 노출시켜 당신의 동의 없이 당신의 팀과 장난을 칠 수 있다는 사실에 비하면 매우 작습니다.

계속해서 다음과 같이 말합니다.

그냥 아무 의미가 없습니다. 축구팀은 선수 목록을 "가지고 있지" 않습니다. 선수 명단입니다. "John McFootballer가 SomeTeam의 선수에 합류했습니다"라고 말하지 않습니다. "John이 SomeTeam에 합류했습니다"라고 말합니다.

당신은 첫 번째 비트에 대해 틀렸습니다. '목록'이라는 단어를 삭제하면 실제로 팀에 선수가 있다는 것이 분명합니다.
그러나 두 번째로 머리에 못을 박았습니다. ateam.Players.Add(...) 호출하는 클라이언트를 원하지 않습니다. 당신은 그들이 ateam.AddPlayer(...) 호출하길 원합니다. 그리고 당신의 구현은 (아마도 무엇보다도) Players.Add(...) 내부적으로 호출할 것입니다.


캡슐화가 객체에 권한을 부여하는 목적에 얼마나 중요한지 알 수 있기를 바랍니다. 각 클래스가 다른 개체의 간섭을 두려워하지 않고 작업을 잘 수행할 수 있도록 하고 싶습니다.


Disillusioned

"팀" 개체 의 동작 에 따라 다릅니다. 컬렉션처럼 작동하는 경우 일반 목록으로 먼저 표시하는 것이 좋습니다. 그러면 목록에서 반복되는 코드를 계속 복제한다는 사실을 알게 될 것입니다. 이 시점에서 플레이어 목록을 래핑하는 FootballTeam 개체를 생성할 수 있습니다. FootballTeam 클래스는 플레이어 목록에서 반복되는 모든 코드의 홈이 됩니다.

그것은 내 코드를 불필요하게 장황하게 만듭니다. 이제 my_team.Count 대신 my_team.Players.Count를 호출해야 합니다. 고맙게도 C#을 사용하면 인덱서를 정의하여 인덱싱을 투명하게 만들고 내부 List의 모든 메서드를 전달할 수 있습니다. 하지만 코드가 너무 많습니다! 나는 그 모든 일에 대해 무엇을 얻습니까?

캡슐화. 고객은 FootballTeam 내부에서 무슨 일이 일어나는지 알 필요가 없습니다. 모든 클라이언트가 알고 있듯이 데이터베이스에서 플레이어 목록을 검색하여 구현할 수 있습니다. 그들은 알 필요가 없으며 이것은 당신의 디자인을 향상시킵니다.

그냥 아무 의미가 없습니다. 축구팀은 선수 목록을 "가지고 있지" 않습니다. 선수 명단입니다. "John McFootballer가 SomeTeam의 선수에 합류했습니다"라고 말하지 않습니다. "John이 SomeTeam에 합류했습니다"라고 말합니다. "문자열의 문자"에 문자를 추가하지 않고 문자열에 문자를 추가합니다. 도서관의 책에 책을 추가하는 것이 아니라 책을 도서관에 추가하는 것입니다.

정확히 :) footballTeam.List.Add(john)가 아니라 FootballTeam.Add(john)라고 말할 것입니다. 내부 목록은 표시되지 않습니다.


xpmatteo

데이터 구조를 나타내는 올바른 C# 방법은 무엇입니까?

"모든 모델이 잘못되었지만 일부는 유용합니다."를 기억하십시오. - 조지 EP 박스

"올바른 방법"은 없으며 유용한 방법만 있습니다.

귀하 및/또는 귀하의 사용자에게 유용한 것을 선택하십시오. 그게 다야 과도하게 엔지니어링하지 말고 경제적으로 개발하십시오. 작성하는 코드가 적을수록 디버그해야 하는 코드가 줄어듭니다. (다음 판을 읽으십시오).

-- 수정됨

내 최선의 대답은 ... 그것은 다릅니다. List에서 상속하면 이 클래스의 클라이언트가 노출되어서는 안 되는 메서드에 노출됩니다. 주로 FootballTeam이 비즈니스 엔터티처럼 보이기 때문입니다.

-- 에디션 2

"과도하게 엔지니어링하지 마십시오"라는 댓글에서 내가 언급한 내용이 기억나지 않습니다. KISS 사고 방식이 좋은 지침이라고 생각하지만List에서 비즈니스 클래스를 상속하면 추상화 누출 로 인해 해결되는 것보다 더 많은 문제가 발생한다는 점을 강조하고 싶습니다.

반면에 단순히 List에서 상속하는 것이 유용한 경우는 제한적이라고 생각합니다. 내가 이전 판에서 썼듯이, 그것은 달려있다. 각 경우에 대한 답은 지식, 경험 및 개인의 선호도에 크게 영향을 받습니다.

답변에 대해 더 정확하게 생각할 수 있도록 도와주신 @kai에게 감사드립니다.


ArturoTena

이것은 "있다"와 "있다"의 트레이드오프를 생각나게 합니다. 때로는 수퍼 클래스에서 직접 상속하는 것이 더 쉽고 더 합리적입니다. 다른 경우에는 독립형 클래스를 만들고 상속받은 클래스를 멤버 변수로 포함하는 것이 더 합리적입니다. 여전히 클래스의 기능에 액세스할 수 있지만 인터페이스 또는 클래스에서 상속할 수 있는 다른 제약 조건에 바인딩되지 않습니다.

당신은 무엇을합니까? 많은 것들과 마찬가지로 ... 컨텍스트에 따라 다릅니다. 내가 사용하는 지침은 다른 클래스에서 상속하려면 "is" 관계가 있어야 한다는 것입니다. 따라서 BMW라는 클래스를 작성하는 경우 BMW는 진정한 자동차이기 때문에 Car에서 상속할 수 있습니다. Horse 클래스는 Mammal 클래스에서 상속할 수 있습니다. 왜냐하면 말은 실제로 실생활에서 포유류이고 모든 Mammal 기능은 Horse와 관련되어야 하기 때문입니다. 그러나 팀이 목록이라고 말할 수 있습니까? 내가 말할 수 있는 바에 따르면 팀은 실제로 "목록"이 아닌 것 같습니다. 따라서 이 경우 List를 멤버 변수로 사용합니다.


Paul J Abernathy

지침에 따르면 공개 API는 목록, 집합, 사전, 트리 등을 사용하는지 여부에 대한 내부 설계 결정을 공개해서는 안 됩니다. "팀"이 반드시 목록일 필요는 없습니다. 목록으로 구현할 수 있지만 공개 API 사용자는 알아야 할 필요에 따라 클래스를 사용해야 합니다. 이를 통해 공개 인터페이스에 영향을 주지 않고 결정을 변경하고 다른 데이터 구조를 사용할 수 있습니다.


QuentinUK

내 더러운 비밀: 나는 사람들이 뭐라고 하든 상관하지 않고 그것을 합니다. .NET Framework는 "XxxxCollection"(내 머리 위 예제의 경우 UIElementCollection)으로 확산됩니다.

그래서 내가 말하는 것을 멈추게 하는 것:

 team.Players.ByName("Nicolas")

내가 그것을 더 잘 찾을 때

 team.ByName("Nicolas")

또한 내 PlayerCollection은 코드 중복 없이 "Club"과 같은 다른 클래스에서 사용될 수 있습니다.

 club.Players.ByName("Nicolas")

어제의 모범 사례가 내일의 모범 사례가 아닐 수도 있습니다. 대부분의 모범 사례 뒤에는 이유가 없으며 대부분은 커뮤니티 간의 광범위한 동의입니다. 커뮤니티가 그렇게 할 때 당신을 비난할지 묻는 대신 스스로에게 물어보십시오. 더 읽기 쉽고 유지 관리가 쉬운 것은 무엇입니까?

 team.Players.ByName("Nicolas")

또는

 team.ByName("Nicolas")

정말로. 의심이 가시나요? 이제 실제 사용 사례에서 List<T>를 사용하지 못하게 하는 다른 기술적 제약을 처리해야 할 수도 있습니다. 그러나 존재해서는 안 되는 제약 조건을 추가하지 마십시오. 마이크로소프트가 그 이유를 문서화하지 않았다면 그것은 분명히 아무데서나 오는 "모범 사례"일 것입니다.


Nicolas Dorier

List<T> 가 "최적화"되었다고 말할 때 나는 그들이 조금 더 비싼 가상 메소드와 같은 기능이 없다는 것을 의미하고 싶다고 생각합니다. 따라서 문제는 공개 API 에서 List<T> 를 노출하면 나중에 비즈니스 규칙을 적용하거나 해당 기능을 사용자 정의할 수 있는 능력을 잃게 된다는 것입니다. 그러나 이 상속된 클래스를 프로젝트 내에서 내부로 사용하는 경우(수천 명의 고객/파트너/다른 팀에 API로 잠재적으로 노출되는 것과 대조적으로) 시간을 절약하고 원하는 기능이면 괜찮을 수 있습니다. 복제하다. List<T> 에서 상속하는 이점은 가까운 장래에 결코 사용자 정의되지 않을 많은 멍청한 래퍼 코드를 제거한다는 것입니다. List<T> 와 정확히 동일한 의미 체계를 갖도록 하려는 경우에도 괜찮을 수 있습니다.

나는 종종 FxCop 규칙이 그렇게 말하거나 누군가의 블로그가 "나쁜" 관행이라고 말합니다. 여러 번, 이것은 코드를 디자인 패턴 palooza 이상하게 만듭니다. 많은 가이드라인과 마찬가지로 예외 가 있을 수 있는 가이드라인으로 취급합니다.


Shital Shah

직렬화 문제

한 측면이 누락되었습니다. List에서 상속되는 클래스는 XmlSerializer를 사용하여 올바르게 직렬화할 수 없습니다. 이 경우 DataContractSerializer를 대신 사용하거나 자체 직렬화 구현이 필요합니다.
 public class DemoList : List<Demo> { // using XmlSerializer this properties won't be seralized // There is no error, the data is simply not there. string AnyPropertyInDerivedFromList { get; set; } } public class Demo { // this properties will be seralized string AnyPropetyInDemo { get; set; } }

추가 읽기: 클래스가 List<>에서 상속되면 XmlSerializer가 다른 특성을 직렬화하지 않습니다.

대신 IList 사용

개인적으로 나는 List에서 상속하지 않고 IList를 구현합니다. Visual Studio가 작업을 수행하고 완전한 작동 구현을 만듭니다. 여기를 보세요: IList를 완벽하게 구현하는 방법


marsh-wiggle

이 답변의 대부분이 복잡한 비교를 가지고 있지는 않지만 이 상황을 처리하는 방법을 공유하고 싶습니다. IEnumerable<T> List<T> 의 모든 메서드와 속성을 공개적으로 노출하지 않고도 Team 클래스가 Linq 쿼리 확장을 지원하도록 할 수 있습니다.

 class Team : IEnumerable<Player> { private readonly List<Player> playerList; public Team() { playerList = new List<Player>(); } public Enumerator GetEnumerator() { return playerList.GetEnumerator(); } ... } class Player { ... }

Null511

저는 그냥 에펠의 발명가이자 계약에 의한 디자인의 발명가인 Bertrand Meyer가 눈꺼풀을 치지 않고도 Team List<Player> 에서 상속받도록 할 것이라고 덧붙이고 싶었습니다.

그의 저서인 Object-Oriented Software Construction 에서 그는 직사각형 윈도우가 자식 윈도우를 가질 수 있는 GUI 시스템의 구현에 대해 논의합니다. 그는 구현을 재사용하기 위해 RectangleTree<Window> 모두에서 Window

그러나 C#은 에펠이 아닙니다. 후자는 다중 상속 및 기능 이름 변경을 지원합니다. C#에서 서브클래스를 만들 때 인터페이스와 구현을 모두 상속합니다. 구현을 재정의할 수 있지만 호출 규칙은 수퍼클래스에서 직접 복사됩니다. 그러나 Eiffel에서는 공개 메서드의 이름을 수정할 수 있으므로 Team 에서 AddRemove 이름을 Hire and Fire 로 변경할 수 있습니다. Team 인스턴스가 List<Player> 다시 업캐스트되면 호출자는 AddRemove 를 사용하여 수정하지만 가상 메서드 HireFire 가 호출됩니다.


Alexey

클래스 사용자가 모든 메서드와 속성을 필요로 하는 경우** List에서 클래스를 파생해야 합니다. 필요하지 않은 경우 목록을 묶고 클래스 사용자가 실제로 필요로 하는 메서드에 대한 래퍼를 만듭니다.

이는 공개 API 또는 많은 사람들이 사용할 다른 코드를 작성하는 경우 엄격한 규칙입니다. 작은 앱이 있고 개발자가 2명 이하인 경우 이 규칙을 무시할 수 있습니다. 이렇게 하면 시간이 절약됩니다.

작은 앱의 경우 덜 엄격한 다른 언어를 선택할 수도 있습니다. Ruby, JavaScript - 적은 양의 코드를 작성할 수 있는 모든 것.


Ivan Nikitin

나는 당신의 일반화에 동의하지 않는다고 생각합니다. 팀은 단순히 선수들의 집합이 아닙니다. 팀에는 이름, 엠블럼, 관리/관리 스태프 모음, 코칭 스태프 모음, 선수 모음 등 훨씬 더 많은 정보가 있습니다. 따라서 FootballTeam 클래스에는 3개의 컬렉션이 있어야 하며 그 자체가 컬렉션이 아니어야 합니다. 실제 세계를 제대로 모델링하려면.

Specialized StringCollection과 같은 몇 가지 다른 기능을 제공하는 PlayerCollection 클래스를 고려할 수 있습니다. 예를 들어 내부 저장소에 개체를 추가하거나 제거하기 전에 유효성 검사 및 검사를 수행하는 것입니다.

아마도 PlayerCollection이라는 개념이 선호하는 접근 방식에 더 적합할까요?

 public class PlayerCollection : Collection<Player> { }

그러면 FootballTeam은 다음과 같이 보일 수 있습니다.

 public class FootballTeam { public string Name { get; set; } public string Location { get; set; } public ManagementCollection Management { get; protected set; } = new ManagementCollection(); public CoachingCollection CoachingCrew { get; protected set; } = new CoachingCollection(); public PlayerCollection Players { get; protected set; } = new PlayerCollection(); }

Eniola

클래스보다 인터페이스 선호

클래스는 클래스에서 파생되는 것을 피하고 대신 필요한 최소한의 인터페이스를 구현해야 합니다.

상속은 캡슐화를 깨뜨립니다.

클래스에서 파생하면 캡슐화가 중단됩니다 .

  • 컬렉션이 구현되는 방식에 대한 내부 세부정보를 노출합니다.
  • 적절하지 않을 수 있는 인터페이스(공개 함수 및 속성 집합)를 선언합니다.

무엇보다도 이것은 코드를 리팩토링하기 어렵게 만듭니다.

클래스는 구현 세부 사항입니다.

클래스는 코드의 다른 부분에서 숨겨야 하는 구현 세부 정보입니다. 간단히 말해서 System.List 는 현재와 미래에 적절할 수도 있고 적절하지 않을 수도 있는 추상 데이터 유형의 특정 구현입니다.

개념적으로 System.List 데이터 형식이 "목록"이라고 하는 사실은 약간 의아합니다. System.List<T> 는 요소를 추가, 삽입 및 제거하기 위한 분할 상환 O(1) 작업과 요소 수를 검색하거나 인덱스별로 요소를 가져오고 설정하기 위한 O(1) 작업을 지원하는 가변 순서 컬렉션입니다.

인터페이스가 작을수록 코드가 유연해집니다.

데이터 구조를 설계할 때 인터페이스가 단순할수록 코드가 더 유연해집니다. LINQ가 이를 입증하는 데 얼마나 강력한지 살펴보십시오.

인터페이스 선택 방법

"목록"을 생각할 때 "나는 야구 선수들의 집합체를 대표해야 한다"라고 스스로에게 말하면서 시작해야 합니다. 따라서 이것을 클래스로 모델링하기로 결정했다고 가정해 보겠습니다. 가장 먼저 해야 할 일은 이 클래스가 노출해야 하는 인터페이스의 최소량을 결정하는 것입니다.

이 프로세스를 안내하는 데 도움이 될 수 있는 몇 가지 질문은 다음과 같습니다.

  • 카운트가 필요합니까? IEnumerable<T> 구현을 고려하지 않는 경우
  • 이 컬렉션은 초기화된 후에 변경됩니까? IReadonlyList<T> 고려하지 않는 경우.
  • 인덱스별로 항목에 액세스할 수 있는 것이 중요합니까? ICollection<T> 고려
  • 컬렉션에 항목을 추가하는 순서가 중요합니까? 아마도 ISet<T> 입니까?
  • 정말로 이것을 원한다면 IList<T> 구현하십시오.

이렇게 하면 코드의 다른 부분을 야구 선수 컬렉션의 구현 세부 사항에 연결하지 않고 인터페이스를 존중하는 한 구현 방법을 자유롭게 변경할 수 있습니다.

이 접근 방식을 사용하면 코드를 읽고, 리팩터링하고, 재사용하기가 더 쉬워집니다.

상용구 피하기에 대한 참고 사항

최신 IDE에서 인터페이스를 구현하는 것은 쉬워야 합니다. 마우스 오른쪽 버튼을 클릭하고 "인터페이스 구현"을 선택하십시오. 그런 다음 필요한 경우 모든 구현을 멤버 클래스로 전달합니다.

즉, 상용구를 많이 작성하고 있다는 사실을 알게 된 경우 잠재적으로 필요한 것보다 더 많은 기능을 노출하고 있기 때문일 수 있습니다. 클래스에서 상속하지 말아야 하는 것과 같은 이유입니다.

또한 애플리케이션에 적합한 더 작은 인터페이스를 디자인할 수 있으며, 이러한 인터페이스를 필요한 다른 인터페이스에 매핑하는 몇 가지 도우미 확장 기능을 사용할 수도 있습니다. 이것은 내가 LinqArray 라이브러리에 대한 IArray 인터페이스에서 취한 접근 방식입니다.


cdiggins

언제 허용됩니까?

Eric Lippert의 말을 인용하자면:

List<T> 메커니즘을 확장 하는 메커니즘을 구축할 때.

예를 들어 IList<T> 에 AddRange 메서드가 AddRange .

 public interface IMoreConvenientListInterface<T> : IList<T> { void AddRange(IEnumerable<T> collection); } public class MoreConvenientList<T> : List<T>, IMoreConvenientListInterface<T> { }

phoog

출처 : http:www.stackoverflow.com/questions/21692193/why-not-inherit-from-listt

반응형