2010-07-01 4 views
6

이전 개발자의 C# 데이터 액세스 코드를 약간 리팩토링했으며 사용 패턴에 대해 궁금합니다.개체에 대한 모든 이점. 개체 [i]에 대한 GetObject (i)?

이 코드는 다양한 ActiveRecord 스타일 비즈니스 개체 (기본적으로 데이터베이스 필드를 래핑하는 개체)의 컬렉션 (배열)을 처음에 노출했습니다. 나는 일반적인 목록에 배열을 변경하고있어,하지만 난에 대해 궁금 코드의 특징은 이전의 개발자가 thusly 히, 그는 포장 된 각 개체 유형에 대한 방법을 취득했다 있다는 것입니다 :

public Thing GetThing(int i) { 
    return things[i]; 
} 

여러 가지가 있습니다 이 방법들 중에서, 나는 나 자신의 삶에 대해 단순히 그 일들을 직접 언급하는 것보다 그 메커니즘을 사용할 때의 어떤 이점을 생각할 수 없다. 논쟁의 여지가 있기 때문에, 물건은 public 필드가 아니라 public 필드입니다 (이 경우 실제로는 자동 구현 된 속성이므로 가정이 사실임).

나는 분명한 뭔가를 놓치고 있습니까? 아니면 뭔가 밀교?

UPDATE 아마 이러한 컬렉션은 현재 루프 내에서 액세스 명확히해야합니다

for (int i = 0; i < thingsCount; i==) { 
    dosomthing(GetThing(i)); 
    dosomethingelse(GetThing(i)); 
} 

난에 리팩토링하고있는 :

for (int i = 0; i < thingsCount; i==) { 
    Thing thing = things[i]; 
    dosomthing(thing); 
    dosomethingelse(thing); 
} 

및 심지어 물건을 사용. 각각().

답변

6

이 분명하면 나도 몰라,하지만 난 당신이 뭔가를 놓치고 생각하십니까.

thingsIList<Thing>이라고 가정 해 보겠습니다. 그런 다음 직접 코드를 호출하면 (Things) 코드를 호출하여 Add, Insert, RemoveAt 등을 호출 할 수 있습니다. 이전 개발자가 이것을 허용하고 싶지 않았거나 어쩌면 충분한 이유가 있음을 확신 할 수 있습니다.

는해도 여전히 클래스의에 따라 허용되어서는 안 작업을 할 수있다 obj.Things[0] = new Thing(); 그런 짓을하는 코드를 호출 할 수있는 것 등을 노출, (등을 사용할 수 없습니다 것입니다, 그래서 Add) 그것은 Thing[]의 랬 이행.

ThingsReadOnlyCollection<Thing>으로 노출하면 이러한 문제를 대부분 해결할 수 있습니다. 그러나 그것이 내려지는 이유는 다음과 같습니다. 개발자 이 코드를 호출하여 인덱스로 항목에 액세스 할 수있게하려는 경우 - 그 이상을 수행 할 수단으로 GetThing 메서드 하나를 제공하면 솔직히 훨씬 더 많이 만듭니다 대부분의 의미.

이제는 get 접근 자로 this[int] 속성을 구현하는 옵션도 있습니다. 그러나 문제의 클래스가 본질적으로 Thing 개체의 컬렉션 인 경우에만 의미가 있습니다. 즉 컬렉션 중 일부에 기타 개체 유형의 컬렉션을 제공하지 않아도됩니다.

모두 들께, 나는 GetThing 접근법이 꽤 좋은 소리라고 생각합니다.

  1. 그/그녀는 또한과 같이 직접 things 수집을 노출 경우 : 다른 꽤 가난한 결정을 당신이 질문을 모호하게 한 방법에서, 그것은 이전의 개발자 같은 소리 않습니다 말했다

    공공 재산, 음, 그럼 ... GetThing 방법의 모든 목적을 이겨내지 않습니까? 결과는 단순히 비 대한 인터페이스 (일반적으로 정당한 이유로 별칭으로 문서화되지 않은 한 똑같은 것을 성취 할 수있는 여러 가지 방법이있을 때 큰 징조는 아니라고 생각합니다.) [업데이트 : 이전 개발자가 이 아니고이 아닙니다. 좋아.]

  2. 내부 개발자 things에서 액세스하는 항목은 GetThing 메서드를 사용하는 것으로 보이지만 이는 내 의견으로는 어리 석다. 클래스 내부에서 클래스의 공용 인터페이스를 사용하여 추가 메서드 호출에 대해 무의미한 오버 헤드가 발생하는 이유는 무엇입니까? 클래스에 있다면 구현에 이미 이 있으며 원하는대로 개인/보호 된 데이터에 액세스 할 수 있으므로 달리 척할 필요가 없습니다.
+0

@ Elpezmuerto의 응답을 통해 생각해 보았습니다. 나는 {collection; *을 속성으로 {get; private set;} 콜렉션을 얻은 후에는 아무런 의미가없는 방식으로 코드를 호출하여 수정할 수 있습니다. 이전 개발자는 이러한 코드를 보호 된 것으로 표시 했으므로 사용자의 포인트 1은 필자의 경우 관련이 없지만 각 루프에서 GetThing (i)을 여러 번 호출했는데 동의하지 않습니다. 최고의 리팩터와 같이 보이는 것은 GetThing() 메서드를있는 그대로두고 루프 한 번만 호출하는 것입니다. 감사합니다. – cori

+0

@cori : 예, 개발자가 클래스를 디자인 할 때 일반적으로 발생하는 오류는 변경 사항이 수정 될 경우 발생할 수있는 상황을 고려하지 않고 변경 가능한 컬렉션을 직접 노출하는 것입니다. 콜렉션 코드가 콜렉션을 열거하고 수정할 수있게하려면 'List '이 아닌'IEnumerable '을 노출 시키십시오! –

+0

@cori : 인덱스에 의한 랜덤 액세스는 허용하지만 'IList '인터페이스의 다른 모든 기능이나 'T []'(항목 설정을 허용하지는 않습니다. , 단지 그들을 얻지 마라). 이것은 실제로 제가 [내 자신의 옛 질문] (http://stackoverflow.com/questions/2110440/why-is-there-no-iarrayt-interface-in-net)에서 잠시 후에 물어 본 것입니다. –

2

여기에서 유의할 사항이 두 가지 있습니다. 첫 번째는 객체 변수를 비공개로 유지하고 getter 및 setter를 사용하여 객체 변수에 액세스하려는 것입니다. 이렇게하면 사용자가 실수로 개체 변수를 변경하거나 수정할 수 없습니다.

둘째, 속성에 직접 액세스 할 때 get/set이라는 용어를 사용해야하는 좋은 명명 규칙으로 간주됩니다. 이렇게하면 가독성이 향상됩니다.

이 경우 공용 속성이지만 getter 및 setter를 사용하면 가독성이 향상되고 예기치 않은 동작을 방지 할 수 있습니다. 루프 내에서 객체 변수에 액세스하는 경우 중요하지 않으므로 GetThing 규칙을 계속 사용해야합니다.

마지막으로 개체 내에서 변수에 액세스하는 경우 getter 또는 setter를 사용할 필요가 없습니다.

참고 : 개인 개체 변수를 유지하고 모든 언어

C++ style guidelines

C# style guidelines

에 대한 게터/세터를 사용하기 위해 일반적으로 간주 좋은 스타일은 또한 질문 "getter and setter for class in class c#"

에 관심이있을 수 있습니다
+0

내가 그 논리를 이해할 수있다. (비록 당신이 가리키는 C# 예제에서 그것을 참조하는 것이 아무것도 보이지 않지만). 감사! – cori

+0

@Cori 업데이트 링크 – Elpezmuerto

2

IList<Thing>으로 개체를 노출하고 싶을 것입니다. 이렇게하면 원하는 인덱싱 기능을 사용할 수 있지만 조건에 따라 새 항목 목록을 만드는 등의 LINQ 기능도 사용할 수 있습니다. 플러스 IList<T>IEnumerable<T>을 구현하므로 foreach을 사용하여 객체를 반복 할 수 있습니다.

수업 :

public IList<Thing> Things { get; private set; } 

사용 :

Thing x = business.Things[3]; 

또는

var x = business.Things.Where(t => t.Name == "cori"); 
+0

정확히 내가 리팩토링으로 List로 향하고있었습니다 (그렇습니다. IList가 아마도 더 나은 선택 일 것입니다). – cori

1

이 개발자가 Java와 같은 다른 언어에 익숙하고 C#의 표준 사례를 완전히 인식하지 못했다고 생각합니다. "get [Property]"명명법은 자바, 자바 스크립트 등에서 많이 사용됩니다. C#은 속성 및 인덱서로 대체합니다. 속성은 getter 및 setter만큼 강력하지만 작성하고 사용하기가 쉽습니다. 당신은 일반적으로 볼 수있는 유일한 시간 "가져가 [뭔가]"C에서 #이있는 경우입니다 :

  • 작업은 당신이 정말로 집이 간단한 멤버 액세스는 없다는 사실을 구동 할만큼 비용이 될 가능성이 높습니다 (예 : GetPrimeNumbers()를) 또는
  • 컬렉션에 실제로 여러 개의 인덱스 된 컬렉션이 포함되어 있습니다. (이 경우에도, 단순히 인덱스 유형 ("table.Rows[2]")의 그 자체에게 속성, 이러한 인덱스 컬렉션의 각을 노출하는 것이 더 일반적이다 예를 들어 GetRow(int i)GetColumn(int i))..

당신이 을 경우 루프에서이 값에 액세스하는 경우에만 컬렉션에 IEnumerable<Thing>을 구현해야 LINQ 메서드와 foreach 구조에 액세스 할 수 있습니다. 여전히 인덱스 기반 getter가 필요한 경우 IEnumerable<T>, 추가로 다음을 제공합니다 :

T this[int i] { get; } 

이렇게하면 소비자는이 컬렉션에 AddRemove 개의 개체를 사용할 수 있다는 인상을주지 못합니다.

업데이트

나는이 주로 논의 될 스타일의 문제, 알고,하지만 난 정말 GetThings 솔루션이 일을하는 올바른 방법이 아니다라고 생각합니다. 이런 식으로 뭔가를 보일 수 있습니다 위의 코드를 사용하여

public class ThingHolderDataAccess 
{ 
    public ThingHolder GetThingHolderForSomeArgs(int arg1, int arg2) 
    { 
     var oneThings = GetOneThings(arg1); 
     var otherThings = GetOtherThings(arg2); 
     return new ThingHolder(oneThings, otherThings); 
    } 
    private IEnumerable<OneThing> GetOneThings(int arg) 
    { 
     //... 
     return new List<OneThing>(); 
    } 
    private IEnumerable<AnotherThing> GetOtherThings(int arg2) 
    { 
     //... 
     return new List<AnotherThing>(); 
    } 
} 

public class ThingHolder 
{ 
    public IIndexedReadonlyCollection<OneThing> OneThings 
    { 
     get; 
     private set; 
    } 

    public IIndexedReadonlyCollection<AnotherThing> OtherThings 
    { 
     get; 
     private set; 
    } 

    public ThingHolder(IEnumerable<OneThing> oneThings, 
         IEnumerable<AnotherThing> otherThings) 
    { 
     OneThings = oneThings.ToIndexedReadOnlyCollection(); 
     OtherThings = otherThings.ToIndexedReadOnlyCollection(); 
    } 
} 

#region These classes can be written once, and used everywhere 
public class IndexedCollection<T> 
    : List<T>, IIndexedReadonlyCollection<T> 
{ 
    public IndexedCollection(IEnumerable<T> items) 
     : base(items) 
    { 
    } 
} 

public static class EnumerableExtensions 
{ 
    public static IIndexedReadonlyCollection<T> ToIndexedReadOnlyCollection<T>(
     this IEnumerable<T> items) 
    { 
     return new IndexedCollection<T>(items); 
    } 
} 

public interface IIndexedReadonlyCollection<out T> : IEnumerable<T> 
{ 
    T this[int i] { get; } 
} 
#endregion 

: 조금 더 많은 작업을 소요하는 동안 다음 전략은 훨씬 더 표준 .NET 클래스와 프레임 워크를 설계하는 방식을 유지에

var things = _thingHolderDataAccess.GetThingHolderForSomeArgs(a, b); 
foreach (var oneThing in things.OneThings) 
{ 
    // do something 
} 
foreach (var anotherThing in things.OtherThings) 
{ 
    // do something else 
} 

var specialThing = things.OneThings[c]; 
// do something to special thing 
+0

문서화 된 응답에 감사드립니다. 내 코드가 동 질적 컬렉션만을 취급하고 있기 때문에 (그리고 내 통제하에 있으며 코드가 내 통제하에있을 뿐이다) 이것은 약간 단순한 니즈에 대한 오버 헤드에 다소 부담이된다. – cori

+0

@cori : 나는 완전히 이해하고있다. 코드가 로컬에서만 사용되는 경우 추가 작업이 필요하지 않습니다. – StriplingWarrior

+0

... 그 경우 워렌 (Warren)과 같은 IList 노출을 제안합니다. GetThing 메서드를 사용할 필요가 없습니다. – StriplingWarrior

0

다른 답변과 마찬가지로 배열 (또는 목록) 자체에 대한 액세스를 제한하는 문제입니다.

일반적으로 클래스의 클라이언트가 배열 자체를 직접 수정할 수 없도록하려고합니다. 기본 목록의 구현을 숨기면 클래스를 사용하는 클라이언트 코드에 영향을 미치지 않고 나중에 구현을 변경할 수 있습니다.