[C#] 개인 필드가 인스턴스가 아닌 유형의 개인 필드 인 이유는 무엇입니까?

C # (및 다른 많은 언어)에서는 동일한 유형의 다른 인스턴스의 개인 필드에 액세스하는 것이 완벽합니다. 예를 들면 다음과 같습니다.

public class Foo
{
    private bool aBool;

    public void DoBar(Foo anotherFoo)
    {
        if (anotherFoo.aBool) ...
    }
}

애즈 C # 1 명세 (섹션 3.5.1, 3.5.2) 전용 필드 액세스 타입이 아닌 경우에 말한다. 동료 와이 문제를 논의하고 있으며 동일한 인스턴스에 대한 액세스를 제한하는 대신 이와 같이 작동하는 이유를 생각해 냈습니다.

우리가 생각해 낼 수있는 가장 좋은 주장은 클래스가 다른 인스턴스와의 동등성을 결정하기 위해 개인 필드에 액세스하려는 동등성 검사입니다. 다른 이유가 있습니까? 아니면 이런 식으로 작동해야한다는 것을 의미하는 황금의 이유 또는 완전히 불가능한 것입니까?



답변

이 방법이 작동하는 한 가지 이유는 액세스 수정자가 컴파일 타임에 작동하기 때문이라고 생각 합니다 . 따라서 주어진 객체가 현재 객체 인지 여부를 결정하는 것은 쉽지 않습니다. 예를 들어 다음 코드를 고려하십시오.

public class Foo
{
    private int bar;

    public void Baz(Foo other)
    {
        other.bar = 2;
    }

    public void Boo()
    {
        Baz(this);
    }
}

컴파일러가 반드시 other 가 실제로 사실을this ? 모든 경우에 해당되는 것은 아닙니다. 컴파일 할 수 없다고 주장 할 수는 있지만 올바른 인스턴스의 프라이빗 인스턴스 멤버 액세스 할 수없는 코드 경로가 있음을 의미 합니다.

객체 수준 가시성보다는 유형 수준 만 요구하면 문제를 다루기 쉬워지고 상황을 해야 작동 실제로 일을.

편집하다 : Danilel Hilgarth의 주장은이 추론이 거꾸로된다는 장점이 있습니다. 언어 디자이너는 원하는 언어를 만들 수 있으며 컴파일러 작성자는이를 준수해야합니다. 즉, 언어 디자이너는 컴파일러 작성자가 작업을보다 쉽게 ​​수행 할 수 있도록 인센티브를 제공합니다. (비록이 경우, 개인 회원이 다음 수 있다고 주장 쉬운 충분 단지 를 통해 액세스 할 this) 암시 적 또는 명시 적으로 ().

그러나 나는 그것이 문제를 필요 이상으로 혼란스럽게 만듭니다. 대부분의 사용자 (자신 포함)는 위의 코드가 작동하지 않으면 불필요하게 제한합니다. 결국 데이터 내가 액세스하려고 해요! 왜 통과 this해야합니까?

간단히 말해, 컴파일러가 “어려운”사례를 과장했을 수도 있습니다. 내가 실제로 극복하려는 것은 위의 상황이 디자이너가 작업하고 싶어하는 것처럼 보인다는 것입니다.


답변

C # 및 유사한 언어에서 사용되는 캡슐화 의 목적이 * 코드의 다른 조각 (C # 및 자바 클래스), 메모리에없는 다른 개체의 상호 의존도를 낮출 수있다.

예를 들어, 한 클래스에서 다른 클래스의 일부 필드를 사용하는 코드를 작성하면 이러한 클래스는 매우 밀접하게 연결됩니다. 그러나 동일한 클래스의 두 객체가있는 코드를 처리하는 경우 추가 종속성이 없습니다. 수업은 항상 그 자체에 달려 있습니다.

그러나 캡슐화에 대한이 모든 이론은 누군가가 속성을 작성하거나 Java에서 get / set 쌍을 작성하고 모든 필드를 직접 노출하는 즉시 실패하므로 클래스가 마치 필드에 액세스하는 것처럼 결합됩니다.

* 캡슐화 종류에 대한 설명은 Abel의 탁월한 답변을 참조하십시오.


답변

이 흥미로운 실에 이미 몇 가지 답변이 추가되었지만 이 행동이 왜 그런지에 대한 진정한 이유를 찾지 못했습니다 . 시도해 보겠습니다.

옛날에

80 년대의 스몰 토크와 90 년대 중반의 자바 사이에서 객체 지향의 개념이 발전했다. 원래 OO에서만 사용할 수있는 개념으로 생각되지 않은 정보 숨기기 (1978 년에 처음 언급 됨)는 클래스의 모든 데이터 (필드)가 개인용이므로 모든 방법이 공개되므로 스몰 토크에 도입되었습니다. 90 년대 OO의 많은 새로운 개발 과정에서 Bertrand Meyer는 그의 획기적인 책 OOSC (Object Oriented Software Construction) 에서 OO 개념의 상당 부분을 공식화하려고 시도했습니다. )에서 OO 개념과 언어 설계에 대한 (거의) 결정적인 참조로 간주 된 .

개인의 가시성의 경우

Meyer에 따르면 정의 된 클래스 세트 (192-193 페이지)에 메소드를 사용할 수 있어야합니다. 이것은 분명히 매우 세분화 된 정보 숨기기를 제공하며, 클래스 A 및 클래스 B 및 모든 하위 항목에 다음 기능을 사용할 수 있습니다.

feature {classA, classB}
   methodName

의 경우에는 private그 다음은 말한다 : 명시 적으로 자신의 클래스에 볼 수와 같은 유형을 선언하지 않고, 당신이 액세스 할 수없는 자격을 갖춘 호출 기능 (방법 / 필드) 그. 즉 x, 변수 인 경우 x.doSomething()허용되지 않습니다. 물론 클래스 자체 내에서 무단 액세스가 허용됩니다.

다시 말해, 같은 클래스의 인스턴스에 의한 액세스를 허용하려면 해당 클래스에 의한 메소드 액세스를 명시 적으로 허용해야합니다. 이를 인스턴스 전용 대 클래스 전용이라고도합니다.

프로그래밍 언어의 인스턴스 전용

클래스 개인 정보 숨기기와는 달리 인스턴스 개인 정보 숨기기를 사용하는 현재 사용중인 두 개 이상의 언어를 알고 있습니다. 하나는 Meyer가 디자인 한 언어 인 Eiffel으로 최대한의 OO를 필요로합니다. 다른 하나는 오늘날 훨씬 더 일반적인 언어 인 루비입니다. 루비에서 “이 인스턴스에 대한 개인 정보”를private 의미합니다. .

언어 디자인을위한 선택

인스턴스 전용을 허용하는 것이 컴파일러에게는 어렵다고 제안되었습니다. 나는 자격있는 메소드 호출을 허용하거나 허용하지 않는 것이 비교적 간단하기 때문에 그렇게 생각하지 않습니다. 개인 방법의 경우 doSomething()허용되며x.doSomething() 되지 않는 경우 언어 디자이너는 개인용 메서드 및 필드에 대한 인스턴스 전용 액세스 가능성을 효과적으로 정의합니다.

기술적 인 관점에서 볼 때 어떤 방법이든 다른 방법을 선택해야 할 이유가 없습니다 (Eiffel.NET이 IL을 사용하여이 작업을 수행 할 수 있다고 생각할 때 (여러 상속에도 불구하고이 기능을 제공하지 않는 고유 한 이유는 없습니다)).

물론 그것은 취향의 문제이며 다른 사람들이 이미 언급했듯이 개인 메서드와 필드에 대한 클래스 수준의 가시성이 없으면 일부 메서드를 작성하는 것이 더 어려울 수 있습니다.

C #에서 클래스 캡슐화 만 허용하고 인스턴스 캡슐화는 허용하지 않는 이유

인스턴스 캡슐화에서 인터넷 스레드를 살펴보면 (언어가 클래스 수준이 아니라 인스턴스 수준에서 액세스 수정자를 정의한다는 사실을 나타내는 데 사용되는 용어 인 경우가 종종 있음) 개념이 찌그러집니다. 그러나 일부 현대 언어는 적어도 개인 액세스 수정 자에 대해 인스턴스 캡슐화를 사용한다는 점을 고려하면 현대 프로그래밍 세계에서 사용할 수 있고 사용 중이라고 생각합니다.

그러나 C #은 언어 디자인을 위해 C ++ 및 Java를 가장 열심히 살펴 보았습니다. Eiffel과 Modula-3도 그림에 있지만 Eiffel missing (다중 상속)의 많은 기능을 고려할 때 개인 액세스 수정 자에 관해서는 Java 및 C ++와 동일한 경로를 선택했다고 생각합니다.

당신이 Eric Lippert, Krzysztof Cwalina, Anders Hejlsberg 또는 C # 표준에서 일한 다른 사람을 잡아야 하는 이유 를 알고 싶다면 . 불행히도 주석 달린 C # 프로그래밍 언어 에서 명확한 메모를 찾을 수 없습니다 .


답변

이것은 내 의견 일 뿐이지 만 실제적으로 프로그래머가 클래스의 소스에 액세스 할 수 있으면 클래스 인스턴스의 비공개 멤버에 액세스하여 클래스를 합리적으로 신뢰할 수 있다고 생각합니다. 프로그래머들이 왼쪽에 이미 왕국 열쇠를 주었을 때 오른손을 묶는 이유는 무엇입니까?


답변

실제로 평등 검사, 비교, 복제, 연산자 과부하 등이 이유입니다. 예를 들어 복잡한 숫자에 operator +를 구현하는 것은 매우 까다로운 작업입니다.


답변

우선, 비공개 정적 멤버는 어떻게됩니까? 정적 메소드로만 액세스 할 수 있습니까? 당신은 확실히 그것을 원하지 않을 것입니다. 왜냐하면 당신은 당신 const의 에 액세스 할 수 없기 때문 입니다.

명백한 질문에 대해서는의 경우를 StringBuilder링크 된 인스턴스 목록으로 구현 한 경우를 고려하십시오 .

public class StringBuilder
{
    private string chunk;
    private StringBuilder nextChunk;
}

자신의 클래스의 다른 인스턴스의 비공개 멤버에 액세스 할 수 없으면 다음 ToString과 같이 구현해야합니다 .

public override string ToString()
{
    return chunk + nextChunk.ToString();
}

이것은 작동하지만 O (n ^ 2)입니다-그리 효율적이지 않습니다. 실제로, 그것은 아마도 StringBuilder수업을 처음부터 시작 한다는 전체 목적을 상실했을 것입니다 . 자신의 클래스의 다른 인스턴스의 개인 멤버에 액세스 할 수있는 경우 ToString적절한 길이의 문자열을 만든 다음 문자열의 해당 위치에 각 청크의 안전하지 않은 복사본을 수행하여 구현할 수 있습니다 .

public override string ToString()
{
    string ret = string.FastAllocateString(Length);
    StringBuilder next = this;

    unsafe
    {
        fixed (char *dest = ret)
            while (next != null)
            {
                fixed (char *src = next.chunk)
                    string.wstrcpy(dest, src, next.chunk.Length);
                next = next.nextChunk;
            }
    }
    return ret;
}

이 구현은 O (n)이므로 매우 빠르며 클래스의 다른 인스턴스의 비공개 멤버에 액세스 할 수있는 경우에만 가능합니다 .


답변

이것은 많은 언어에서 완벽하게 합법적입니다 (하나는 C ++). 액세스 수정자는 OOP의 캡슐화 원칙에서 비롯됩니다. 아이디어는 외부 액세스를 제한하는 것입니다 .이 경우 outside는 다른 클래스입니다. 예를 들어 C #의 중첩 클래스는 부모 개인 멤버도 액세스 할 수 있습니다.

이것은 언어 디자이너를위한 디자인 선택이지만. 이 액세스의 제한은 엔터티의 격리에 많은 기여를하지 않으면 서 매우 일반적인 일부 시나리오를 극도로 복잡하게 만들 수 있습니다.

비슷한 토론이 있습니다