getter 전용 속성을 재정의하고 setter를 추가 할 수없는 이유는 무엇입니까? [닫은]


141

다음 C # 코드가 허용되지 않는 이유 :

public abstract class BaseClass
{
    public abstract int Bar { get;}
}

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get { return 0; }
        set {}
    }
}

CS0546 'ConcreteClass.Bar.set': 'BaseClass.Bar'에 재정의 가능한 set 접근자가 없기 때문에 재정의 할 수 없습니다.


9
StackOverflow는 Microsoft를 대신하여 질문에 답변하는 기능이 제한적입니다. 질문을 다시 생각해보십시오.
Jay Bazuzi

1
또한, .net에서 이러한 성가신 동작에 대한 간단한 해결책 Foo은 보호되지 않은 추상 GetFoo메소드를 래핑하는 것 외에는 아무것도 아닌 비가 상 읽기 전용 속성을 갖는 것입니다. 추상 파생 형식은 위에서 언급 한 GetFoo방법을 추상 SetFoo방법 과 함께 래핑하는 것 외에는 아무것도 아닌 비가 상 읽기-쓰기 속성으로 속성을 섀도 잉 할 수 있습니다 . 구체적인 파생 형식은 위의 작업을 수행 할 수 있지만 GetFoo및에 대한 정의를 제공 할 수 SetFoo있습니다.
supercat

1
C ++ / CLI는 불에 연료를 추가하기 위해이를 허용합니다.
ymett

1
속성을 재정의 할 때 접근자를 추가하는 기능은 향후 C # 버전 ( github.com/dotnet/roslyn/issues/9482 )에 대한 변경으로 제안되었습니다 .
Brandon Bonds

1
솔직히 말해서,이 질문은 종종 읽기 전용 기본 클래스, 파생 된 가변 클래스와 같은 패턴을 사용하는 기존 라이브러리 유형을 확장해야 할 때 발생합니다. 라이브러리가 이미 잘못된 패턴 (예 : WPF)을 사용하므로이 잘못된 패턴을 해결해야합니다. 강의는 하루가 끝날 때 해결 방법을 구현할 필요가 없습니다.
rwong

답변:


-4

Baseclass의 작성자는 Bar가 읽기 전용 특성이어야한다고 명시 적으로 선언했기 때문입니다. 파생이이 계약을 위반하고 읽기 / 쓰기로 만드는 것은 의미가 없습니다.

나는 이것에 대해 Microsoft와 함께 있습니다.
베이스 클래스 파생에 대해 코드를 작성하라는 새로운 프로그래머라고 가정 해 봅시다. Bar가 쓸 수 없다고 가정하는 무언가를 작성하십시오 (Baseclass가 명시 적으로 get only 속성이라고 명시하기 때문에). 이제 당신의 파생으로 내 코드가 깨질 수 있습니다. 예 :

public class BarProvider
{ BaseClass _source;
  Bar _currentBar;

  public void setSource(BaseClass b)
  {
    _source = b;
    _currentBar = b.Bar;
  }

  public Bar getBar()
  { return _currentBar;  }
}

Bar는 BaseClass 인터페이스에 따라 설정할 수 없으므로 BarProvider는 캐싱이 안전한 것으로 가정합니다. Bar는 수정할 수 없기 때문입니다. 그러나 파생에서 설정이 가능한 경우 누군가가 _source 객체의 Bar 속성을 외부에서 수정 한 경우이 클래스는 오래된 값을 제공 할 수 있습니다. 요점은 ' 개방하라, 비열한 일이나 사람들을 놀라게하지 말라 '

업데이트 : Ilya Ryzhenkov가 '왜 인터페이스가 같은 규칙으로 재생되지 않습니까?' 흠 .. 내가 생각할 때 더 무거워진다.
인터페이스는 'Bar라는 이름의 읽기 속성을 구현할 것으로 예상됩니다'라는 계약입니다. 개인적 으로 인터페이스를 본다면 읽기 전용이라고 가정 할 가능성이 훨씬 적습니다. 인터페이스에서 get-only 속성을 볼 때 기본 클래스에서 '모든 구현 이이 속성을 표시합니다'로 읽습니다 .'Bar is a read-only property '로 클릭합니다. 물론 기술적으로 계약을 위반하지는 않습니다. 더 많은 일을하고 있습니다. 그래서 당신은 어떤 의미에서 옳습니다. 나는 '오해가 자랄 수 있도록 가능한 한 어렵게 만드십시오'라고 말함으로써 가깝습니다.


94
나는 아직도 확신하지 않다. 명시적인 setter가 없더라도 속성이 항상 같은 객체를 반환한다고 보장하지는 않습니다. 다른 방법으로도 변경할 수 있습니다.
ripper234

34
그렇다면 인터페이스에 대해서도 동일하지 않아야합니까? 인터페이스에는 읽기 전용 속성이 있고 구현 유형에는 읽기 / 쓰기가 가능합니다.
Ilya Ryzhenkov

49
동의하지 않습니다. 기본 클래스는 속성이 setter가없는 것으로 선언했습니다. 읽기 전용 인 속성과 다릅니다. "읽기 전용"은 사용자에게 할당 된 의미 일뿐 입니다. C #에는 관련 보증이 전혀 없습니다. 매번 변경되는 get-only 속성 또는 setter가을 throw하는 get / set 속성을 가질 수 있습니다 InvalidOperationException("This instance is read-only").
Roman Starkov

21
getter와 setter를 메소드로 보면 새로운 메소드를 추가하는 것보다 더 이상 "계약 위반"이어야하는 이유를 알 수 없습니다. 베이스 수준의 계약은 기능이 무엇인지와는 아무 상관이 없다 하지 것만으로 존재 입니다 선물을.
Dave Cousineau 1

37
-1이 답변에 동의하지 않고 오해의 소지가 있습니다. 기본 클래스는 모든 클래스가 준수해야하는 동작을 정의하므로 (Liskov의 대체 원칙 참조) 동작을 추가하도록 제한하지 않아야합니다. 기본 클래스는 동작이 무엇인지 만 정의 할 수 있으며 동작이 '하지 말아야 할 것'(즉, '구체적인 수준에서 쓰기 가능하지 않아야 함')을 지정할 수 없습니다.
Marcel Valdez Orozco

39

주된 이유는 구문이 다른 방식으로 작동하기에 너무 명시 적이라고 생각합니다. 이 코드는 :

public override int MyProperty { get { ... } set { ... } }

get과 및 모두 set가 재정의 됨을 매우 분명합니다 . 더 없다 set컴파일러는 불평 있도록 기본 클래스에서. 기본 클래스에 정의되어 있지 않은 메서드를 재정의 할 수없는 것처럼 setter도 재정의 할 수 없습니다.

컴파일러가 의도를 추측하고 재정의 할 수있는 메소드 (이 경우 게터)에만 재정의를 적용해야한다고 말할 수도 있지만 C # 디자인 원칙 중 하나에 위배됩니다. 컴파일러는 의도를 추측해서는 안됩니다 모르게 잘못 추측 할 수 있기 때문입니다.

에릭 리퍼 (Eric Lippert)가 말한 것처럼 다음과 같은 구문은 훌륭하게 작동 할 수 있습니다.

public int MyProperty
{
    override get { ... }
    set { ... }
}

또는 자동 구현 된 속성의 경우

public int MyProperty { override get; set; }

19

나는 오늘도 똑같은 문제를 우연히 발견했으며 이것을 원할만한 타당한 이유가 있다고 생각합니다.

먼저 get-only 속성이 반드시 읽기 전용으로 변환되는 것은 아니라고 주장하고 싶습니다. "이 인터페이스 / 추상에서이 값을 얻을 수 있습니다"로 해석합니다. 이는 해당 인터페이스 / 추상 클래스의 일부 구현에서이 값을 명시 적으로 설정하기 위해 사용자 / 프로그램이 필요하지 않음을 의미하지 않습니다. 추상 클래스는 필요한 기능의 일부를 구현하는 목적을 제공합니다. 상속 된 클래스가 계약을 위반하지 않고 세터를 추가 할 수없는 이유는 전혀 없습니다.

다음은 오늘 필요한 것의 간단한 예입니다. 나는이 문제를 해결하기 위해 인터페이스에 세터를 추가해야했습니다. SetProp 메소드를 추가하지 않고 setter를 추가하지 않는 이유는 Prop의 직렬화에 인터페이스의 특정 구현이 DataContract / DataMember를 사용했기 때문에 목적을 위해 다른 속성을 추가 해야하는 경우 불필요하게 복잡해 졌기 때문입니다. 직렬화

interface ITest
{
    // Other stuff
    string Prop { get; }
}

// Implements other stuff
abstract class ATest : ITest
{
    abstract public string Prop { get; }
}

// This implementation of ITest needs the user to set the value of Prop
class BTest : ATest
{
    string foo = "BTest";
    public override string Prop
    {
        get { return foo; }
        set { foo = value; } // Not allowed. 'BTest.Prop.set': cannot override because 'ATest.Prop' does not have an overridable set accessor
    }
}

// This implementation of ITest generates the value for Prop itself
class CTest : ATest
{
    string foo = "CTest";
    public override string Prop
    {
        get { return foo; }
        // set; // Not needed
    }
}

나는 이것이 단지 "나의 2 센트"의 게시물이라는 것을 알고 있지만, 나는 원래의 포스터와 함께 느끼고 이것이 좋은 것임을 합리화하려고 노력합니다. 특히 이상한 것을 직접 상속 할 때 동일한 제한이 적용되지 않는다는 점을 고려하면 상호 작용.

또한 재정의 대신 new를 사용하는 것에 대한 언급은 여기에 적용되지 않으며 단순히 작동하지 않으며 인터페이스를 통해 설명 된 가상 게터와 같은 원하는 결과를 얻지 못합니다.


2
또는 제 경우에는 {get; 개인 세트; }. 세트가 비공개로 유지되고 파생 상품 클래스에만 관심이 있기 때문에 계약을 위반하지 않습니다.
Machtyn

16

있을 수있다

TL; DR - 당신이 원한다면 당신은 세터와 GET-유일한 방법을 재정의 할 수 있습니다. 기본적으로는 다음과 같습니다.

  1. 같은 이름을 사용하여 newa get와 a set를 모두 가진 속성을 만듭니다 .

  2. 다른 작업을 수행하지 않으면 get파생 클래스가 기본 유형을 통해 호출 될 때 이전 메소드가 계속 호출됩니다. 이 문제를 해결하려면 이전 메소드에서 abstract사용 하는 중간 계층을 추가 하여 새 메소드의 결과 를 강제로 리턴하십시오 .overridegetget

이를 통해 기본 정의에 속성 이없는 경우에도 get/로 속성을 재정의 할 수 있습니다 set.

보너스로 원하는 경우 반환 유형을 변경할 수도 있습니다.

  • 기본 정의가 get-only 인 경우 더 파생 된 리턴 유형을 사용할 수 있습니다.

  • 기본 정의가 set-only 인 경우 덜 파생 된 반환 유형을 사용할 수 있습니다.

  • 기본 정의가 이미 get/ set이면 다음을 수행하십시오.

    • 당신은 더 파생 반환 형식을 사용할 수있는 경우에 당신이 그것을 만들 set오닐;

    • 당신은 덜 파생 반환 형식을 사용할 수있는 경우에 당신이 그것을 만들 get오닐.

모든 경우에 원하는 경우 동일한 반환 유형을 유지할 수 있습니다. 아래 예제는 단순화를 위해 동일한 리턴 유형을 사용합니다.

상황 : 기존 get전용 속성

수정할 수없는 클래스 구조가 있습니다. 아마도 하나의 클래스 일 수도 있고 기존 상속 트리 일 수도 있습니다. 어떤 경우이든 set속성에 메서드를 추가하려고 하지만 할 수는 없습니다.

public abstract class A                     // Pre-existing class; can't modify
{
    public abstract int X { get; }          // You want a setter, but can't add it.
}
public class B : A                          // Pre-existing class; can't modify
{
    public override int X { get { return 0; } }
}

문제 : 할 수 없습니다 - 단지와 /overridegetgetset

당신이 원하는 overrideA를 get/ set부동산,하지만 컴파일되지 않습니다.

public class C : B
{
    private int _x;
    public override int X
    {
        get { return _x; }
        set { _x = value; }   //  Won't compile
    }
}

솔루션 : abstract중간 레이어 사용

당신이 할 수있는 직접적 동안 overrideA를 get/ set재산, 당신은 할 수 있습니다 :

  1. 같은 이름 으로 new get/ set속성을 만듭니다 .

  2. override이전 get새에 대한 접근과 방법 get방법은 일관성을 보장합니다.

따라서 먼저 abstract중간 계층 을 작성하십시오 .

public abstract class C : B
{
    //  Seal off the old getter.  From now on, its only job
    //  is to alias the new getter in the base classes.
    public sealed override int X { get { return this.XGetter; }  }
    protected abstract int XGetter { get; }
}

그런 다음 이전에 컴파일하지 않은 클래스를 작성하십시오. 실제로 overrideget-only 속성을 사용 하지 않기 때문에 이번에는 컴파일 됩니다. 대신 new키워드를 사용하여 교체하고 있습니다.

public class D : C
{
    private int _x;
    public new virtual int X { get { return this._x; } set { this._x = value; } }

    //  Ensure base classes (A,B,C) use the new get method.
    protected sealed override int XGetter { get { return this.X; } }
}

결과 : 모든 것이 작동합니다!

분명히 이것은 의도 한대로 작동합니다 D.

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

test.X = 7;
Print(test.X);      // Prints "7", as intended.

D기본 클래스 중 하나로 볼 때 모든 것이 여전히 의도 한대로 작동합니다 ( 예 : A또는) B. 그러나 그것이 작동하는 이유는 조금 덜 분명 할 수 있습니다.

var test = new D() as B;
//test.X = 7;       // This won't compile, because test looks like a B,
                    // and B still doesn't provide a visible setter.

그러나의 기본 클래스 정의 get는 여전히 파생 클래스의 정의에 의해 재정의 get되므로 여전히 완전히 일관성이 있습니다.

var test = new D();
Print(test.X);      // Prints "0", the default value of an int.

var baseTest = test as A;
Print(test.X);      // Prints "7", as intended.

토론

이 방법을 사용하면 속성 setget전용 속성 에 추가 할 수 있습니다 . 그것을 사용하여 다음과 같은 작업을 수행 할 수도 있습니다.

  1. 에 어떤 속성을 변경 get- 단지, set오닐, 또는 get- 및 - set관계없이 기본 클래스에 무엇의 속성입니다.

  2. 파생 클래스에서 메소드의 리턴 유형을 변경하십시오.

주요 단점은 더 많은 코딩이 있고 abstract class상속 트리에 추가가 있다는 것 입니다. 매개 변수를 사용하는 생성자는 중간 계층에서 복사 / 붙여 넣기해야하기 때문에 약간 성 가실 수 있습니다.


이 질문에 스스로 답변을 주셨습니다 : stackoverflow.com/questions/22210971
Nat

좋은 접근법 (+1). 그러나 이것이 엔티티 프레임 워크에서 원활하게 작동하는지 의심합니다.
Mike de Klerk

9

파생 형식에서 getter를 재정의 할 수없는 것이 안티 패턴이라는 데 동의합니다. Read-Only는 순수한 기능 계약 (최고의 투표 답변으로 암시 됨)이 아닌 구현 부족을 지정합니다.

동일한 오해가 촉진되었거나 문법 단순화로 인해 Microsoft에 이러한 제한이 있다고 생각합니다. 그러나 이제 범위를 적용하여 개별적으로 가져 오거나 설정할 수 있습니다. 아마도 재정의도 가능할 수 있기를 바랍니다.

최고 투표 답변에 따르면 오해는 읽기 전용 속성이 읽기 / 쓰기 속성보다 "순수"해야한다는 것입니다. 프레임 워크에서 많은 일반적인 읽기 전용 속성을 살펴보십시오. 값은 상수 / 순수하게 기능하지 않습니다. 예를 들어 DateTime.Now는 읽기 전용이지만 순수한 기능 값을 제외한 모든 것입니다. 다음 번에 같은 값을 반환한다고 가정하면 읽기 전용 속성의 값을 '캐시'하는 것은 위험합니다.

어쨌든, 나는이 한계를 극복하기 위해 다음 전략 중 하나를 사용했습니다. 둘 다 완벽하지는 않지만이 언어 부족을 뛰어 넘을 수 있습니다.

   class BaseType
   {
      public virtual T LastRequest { get {...} }
   }

   class DerivedTypeStrategy1
   {
      /// get or set the value returned by the LastRequest property.
      public bool T LastRequestValue { get; set; }

      public override T LastRequest { get { return LastRequestValue; } }
   }

   class DerivedTypeStrategy2
   {
      /// set the value returned by the LastRequest property.
      public bool SetLastRequest( T value ) { this._x = value; }

      public override T LastRequest { get { return _x; } }

      private bool _x;
   }

DateTime.Now는 부작용이 없다는 점에서 순전히 기능적입니다. 다른 방법의 부작용은 여기에서도 고려하지 않습니다).
supercat

1
안녕 슈퍼 캣. 함수가 순수하지 못하게하는 외부 적으로 관찰 가능한 부작용에 대해서는 정확하지만 다른 제약은 순수 함수 결과 값은 매개 변수 에만 의존 해야한다는 입니다. 따라서 함수로서 순수한 정적 속성은 변경 불가능하고 일정해야합니다. 정교한 컴파일러는 매개 변수없이 순수한 함수에 대한 모든 후속 호출을 첫 번째 호출에서 반환 한 결과로 바꿀 수 있습니다.
T.Tobler

내 용어가 정확하지 않다는 것이 맞지만, 방법이 아닌 읽기 전용 속성의 적합성은 순도보다는 부작용의 영향에 달려 있다고 생각합니다.
supercat

2

새 속성을 만들어 문제를 해결할 수도 있습니다.

public new int Bar 
{            
    get { return 0; }
    set {}        
}

int IBase.Bar { 
  get { return Bar; }
}

4
그러나 기본 클래스에서 상속 할 때가 아니라 인터페이스를 구현할 때만이를 수행 할 수 있습니다. 다른 이름을 선택해야합니다.
svick

예제 클래스가 IBase (공용 인터페이스 IBase {int Bar {get;}}와 같은 것)를 구현한다고 가정합니다. 그렇지 않으면 클래스를 구현할 때 int IBase.Bar {...}가 허용되지 않습니다. 그런 다음 get과 set을 모두 사용하여 일반 속성 막대를 간단히 만들 수 있습니다. 인터페이스의 Bar 속성에는 세트가 필요하지 않습니다.
이브라힘 벤 살라

1

모든 요점을 이해할 수는 있지만 실제로 C # 3.0의 자동 속성은 쓸모가 없습니다.

당신은 그런 식으로 할 수 없습니다 :

public class ConcreteClass : BaseClass
{
    public override int Bar
    {
        get;
        private set;
    }
}

IMO, C #은 그러한 시나리오를 제한해서는 안됩니다. 적절하게 사용하는 것은 개발자의 책임입니다.


당신의 요점을 이해하지 못합니다. C #이 세터를 추가 할 수 있어야한다고 동의합니까, 아니면이 질문에 답변 한 다른 사람들과 대부분의 관계에 있습니까?
ripper234

2
내가 말했듯이 IMO, C #은 setter 추가를 허용해야합니다. 적절하게 사용하는 것은 개발자의 책임입니다.
Thomas Danecker

1

문제는 어떤 이유로 든 Microsoft가 읽기 전용, 쓰기 전용 및 읽기 / 쓰기의 세 가지 고유 한 유형의 속성이 있어야한다고 결정한 이유입니다.이 컨텍스트 중 하나만 지정된 컨텍스트에 지정된 서명으로 존재할 수 있습니다. 속성은 동일하게 선언 된 속성으로 만 재정의 될 수 있습니다. 원하는 것을 수행하려면 동일한 이름과 서명을 가진 두 가지 속성 (하나는 읽기 전용이고 다른 하나는 읽기 / 쓰기)을 만들어야합니다.

개인적으로 "속성"의 전체 개념을 폐지 할 수 있기를 바랍니다. 단, 속성 구문은 "get"및 "set"메소드를 호출하는 구문 설탕으로 사용될 수 있습니다. 이것은 'add set'옵션을 용이하게 할뿐만 아니라 'get'이 'set'과 다른 유형을 반환하도록 허용합니다. 이러한 기능은별로 자주 사용되지 않지만 'get'메소드가 랩퍼 오브젝트를 리턴하는 반면 'set'은 랩퍼 또는 실제 데이터를 허용 할 수 있습니다.


0

Reflection을 사용하여이를 달성하기위한 해결 방법은 다음과 같습니다.

var UpdatedGiftItem = // object value to update;

foreach (var proInfo in UpdatedGiftItem.GetType().GetProperties())
{
    var updatedValue = proInfo.GetValue(UpdatedGiftItem, null);
    var targetpropInfo = this.GiftItem.GetType().GetProperty(proInfo.Name);
    targetpropInfo.SetValue(this.GiftItem, updatedValue,null);
}

이런 식으로 읽기 전용 인 속성에서 객체 값을 설정할 수 있습니다. 모든 시나리오에서 작동하지 않을 수 있습니다!


1
설명 된 게터 전용 속성에서와 같이 존재하지 않는 리플렉션을 통해 세터를 호출 할 수 있습니까?
또는 매퍼

0

질문의 제목이 추상 속성을 재정의하는 것과 관련되거나 질문이 클래스의 get-only 속성을 재정의하는 것과 관련된 세부 사항으로 질문 제목을 변경해야합니다.


전자의 경우 (추상 속성을 재정의)

그 코드는 쓸모가 없습니다. 기본 클래스만으로는 Get-Only 속성 (아마도 인터페이스)을 재정의하도록 강요해서는 안됩니다. 기본 클래스는 구현 클래스의 특정 입력이 필요할 수있는 공통 기능을 제공합니다. 따라서 공통 기능은 추상 속성이나 메서드를 호출 할 수 있습니다. 주어진 경우 일반적인 기능 방법은 다음과 같은 추상 방법을 재정의하도록 요구해야합니다.

public int GetBar(){}

그러나 그에 대한 통제력이없고 기본 클래스의 기능이 자체 공용 속성 (이상)을 읽는다면 다음과 같이하십시오.

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    private int _bar;
    public override int Bar
    {
        get { return _bar; }
    }
    public void SetBar(int value)
    {
        _bar = value;
    }
}

나는 (이상한) 의견을 지적하고 싶습니다 : 모범 사례는 클래스가 자체 공용 속성을 사용하지 않고 개인 / 보호 필드가있을 때 사용하는 것입니다. 따라서 이것은 더 나은 패턴입니다.

public abstract class BaseClass {
    protected int _bar;
    public int Bar { get { return _bar; } }
    protected void DoBaseStuff()
    {
        SetBar();
        //Do something with _bar;
    }
    protected abstract void SetBar();
}

public class ConcreteClass : BaseClass {
    protected override void SetBar() { _bar = 5; }
}

후자의 경우 (클래스의 get-only 속성을 재정의)

모든 비추 상 속성에는 세터가 있습니다. 그렇지 않으면 쓸모가 없으므로 사용하지 않아도됩니다. Microsoft는 귀하가 원하는 것을 수행하도록 허용하지 않습니다. 이유 : 세터는 어떤 형태 나 다른 형태로 존재하며 원하는 Veerryy를 쉽게 달성 할 수 있습니다.

기본 클래스, 또는 당신이 가진 속성을 읽을 수있는 모든 클래스 {get;},이 일부 해당 속성에 대한 노출 세터의 종류. 메타 데이터는 다음과 같습니다.

public abstract class BaseClass
{
    public int Bar { get; }
}

그러나 구현에는 복잡한 스펙트럼의 두 가지 끝이 있습니다.

최소 복합물 :

public abstract class BaseClass
{
    private int _bar;
    public int Bar { 
        get{
            return _bar;
        }}
    public void SetBar(int value) { _bar = value; }
}

가장 복잡한 :

public abstract class BaseClass
{
    private int _foo;
    private int _baz;
    private int _wtf;
    private int _kthx;
    private int _lawl;

    public int Bar
    {
        get { return _foo * _baz + _kthx; }
    }
    public bool TryDoSomethingBaz(MyEnum whatever, int input)
    {
        switch (whatever)
        {
            case MyEnum.lol:
                _baz = _lawl + input;
                return true;
            case MyEnum.wtf:
                _baz = _wtf * input;
                break;
        }
        return false;
    }
    public void TryBlowThingsUp(DateTime when)
    {
        //Some Crazy Madeup Code
        _kthx = DaysSinceEaster(when);
    }
    public int DaysSinceEaster(DateTime when)
    {
        return 2; //<-- calculations
    }
}
public enum MyEnum
{
    lol,
    wtf,
}

내 요점은 어느 쪽이든, 당신은 세터를 노출시켰다. 귀하의 경우, int Bar기본 클래스가 처리하지 않기를 원하거나, 처리 방법을 검토 할 수있는 권한이 없거나, 어떤 코드를 당신의 의지에 비해 실제로 신속하게 처리 해야하는 작업을 수행하지 않았기 때문에 재정의 할 수 있습니다 .

후기와 이전 (결론)

Long-Story Short : Microsoft는 아무것도 변경할 필요가 없습니다. 구현 클래스를 설정하는 방법을 선택하고 생성자를 산정하여 기본 클래스를 모두 사용하거나 전혀 사용하지 않을 수 있습니다.


0

그럼에도 불구하고 사용 사례의 작은 하위 집합에 대한 솔루션이지만 C # 6.0에서는 "읽기 전용"setter가 재정의 된 getter 전용 속성에 대해 자동으로 추가됩니다.

public abstract class BaseClass
{
    public abstract int Bar { get; }
}

public class ConcreteClass : BaseClass
{
    public override int Bar { get; }

    public ConcreteClass(int bar)
    {
        Bar = bar;
    }
}

-1

캡슐화 및 구현 숨기기 개념을 깨뜨릴 수 있기 때문입니다. 클래스를 생성하고 배송하면 클래스 소비자가 원래 게터 만 제공하는 속성을 설정할 수 있습니다. 구현에 의존 할 수있는 클래스의 불변을 효과적으로 방해합니다.


1
-1 : 세터가 있다고해서 자손이 변하지 않는 것을 자동으로 활성화하지는 않습니다. 세터는 여전히 자손에 의해 이미 변경 가능한 것들만 변경할 수 있습니다.
Roman Starkov

@RomanStarkov 사실입니다. 그러나이 게시물의 문제는 다음과 같습니다. 계약하지 않은 추상화에 세터를 추가 할 수없는 이유는 무엇입니까? 당신의 의견은 반대되는 질문에 적절할 것입니다. 자손이 개인 필드를 완전히 제어 할 수있는 경우 Microsoft가 추상화에서 ReadOnly 속성을 만들 수있는 이유는 무엇입니까?
Suamere

+1 :이 질문은 Microsoft가 계약 위반을 허용하지 않는 이유에 관한 것입니다. 다운 보트의 이유는 Microsoft가 계약에 읽기 전용 속성 만 포함하도록 허용하는 이유에 대한 것입니다. 따라서이 답변은 맥락에서 정확합니다.
Suamere

@Suamere는 계약서에 재산을 변경할 수 없다고 명시하지 않았습니다. 그것이 읽을 수있는 속성이 존재한다는 것입니다. 세터를 추가해도이 계약은 중단되지 않습니다. 당신은 할 수 있습니다 해석의도 읽기 전용 속성을 만들 수 있지만, 당신은 그것을 변경할 수있는 방법이며, 실제로 거기있는 거 생각 때문에 계약 당신이 거기 없다는 것을 C # 6에서 보장 할 수 없습니다. 인터페이스에 대해 생각해보십시오. gettable (get-only!) 속성에 대한 계약을 제공 할 수 있습니다. 이 인터페이스는 get + set 속성으로 구현할 수 있습니다.
로마 Starkov

구체적인 저자의 관점에서이 문제에 접근하고 있습니다. 그는 원하는대로 변경할 수 있습니다. 기본 클래스 작성자-> 구현 클래스의 최종 결과 소비자로 접근하는 관점에서 접근하고 있습니다. 기본 클래스의 작성자는 미래의 구체적인 클래스의 소비자가 구체적인 클래스의 작성자가 사례별로 처리하는 특별한 방법을 노출하지 않고 해당 속성을 변경할 수 있기를 원하지 않습니다. 기본 클래스에서 해당 속성은 변경할 수없는 것으로 취급되므로 속성을 노출하는 것만으로 전달됩니다.
Suamere

-1

불가능하지 않습니다. 속성에서 "새"키워드를 사용하면됩니다. 예를 들어

namespace {
    public class Base {
        private int _baseProperty = 0;

        public virtual int BaseProperty {
            get {
                return _baseProperty;
            }
        }

    }

    public class Test : Base {
        private int _testBaseProperty = 5;

        public new int BaseProperty {
            get {
                return _testBaseProperty;
            }
            set {
                _testBaseProperty = value;
            }
        }
    }
}

이 접근법이이 논의의 양쪽을 만족시키는 것처럼 보입니다. "new"를 사용하면 기본 클래스 구현과 하위 클래스 구현 간의 계약이 깨집니다. 이는 클래스가 여러 계약 (인터페이스 또는 기본 클래스를 통해)을 가질 수있는 경우에 필요합니다.

도움이 되었기를 바랍니다


29
이 속성은 표시된 속성이 new더 이상 가상 기반 속성을 재정의하지 않기 때문에 잘못되었습니다 . 특히, 기본 게시물이 원래 게시물에서와 같이 abstractnew경우 모든 추상 멤버를 재정의해야하므로 추상이 아닌 서브 클래스에서 키워드 를 사용할 수 없습니다 .
Timwi

: 다음 리턴 같은 객체 임에도 불구하고 다른 결과가 있기 때문 급격하게 위험하다 Test t = new Test(); Base b = t; Console.WriteLine(t.BaseProperty), 당신에게 5를 제공 Console.WriteLine(b.BaseProperty)당신에게 공을 제공하고, 후임이 디버깅해야 할 때 당신이 더 멀리, 멀리 이동 한 것입니다.
Mark Sowul

마크에 동의합니다. 이것은 매우 불쾌합니다. 두 BaseProperty 구현이 동일한 변수에 의해 뒷받침되면 IMHO를 사용하는 것이 좋습니다. IE : _baseProperty를 보호하고 _testBaseProperty를 폐지하십시오. 또한 Base의 AFAIK 구현은 실제로 재정의하지 않기 때문에 가상 일 필요는 없습니다.
Steazy

질문에 대한 대부분의 답변은 올바른 의견과 의견을 가질 수 있습니다. 그러나 이전의 의견과는 별도로,이 답변에 대한 나의 주된 비판은 실제 질문이 소비자가 통제 할 수없는 코드베이스의 추상적 속성에 관한 것입니다. 따라서 저는이 답변이 질문에 맞지 않을 수 있다는 비판을 받고 있습니다.
Suamere

-1

기본 클래스의 읽기 전용 속성은이 속성이 클래스 내에서 항상 확인할 수있는 값 (예 : 객체의 (db-) 컨텍스트와 일치하는 열거 형 값)을 나타냅니다. 따라서 가치를 결정하는 책임은 수업 내에서 유지됩니다.

setter를 추가하면 여기에서 어색한 문제가 발생합니다. 값을 이미 보유한 단일 값 이외의 값으로 설정하면 유효성 검사 오류가 발생합니다.

그러나 규칙에는 종종 예외가 있습니다. 예를 들어 하나의 파생 클래스에서 컨텍스트가 가능한 열거 형 값을 10에서 3으로 좁히는 것이 가능하지만이 객체의 사용자는 여전히 어느 것이 올바른지를 결정해야합니다. 파생 클래스는 값을 결정하는 책임을이 개체의 사용자에게 위임해야합니다. 인식하는 것이 중요은 이 객체의 사용자가 있다는 것입니다 이 예외 잘 알고 있어야 하고 올바른 값을 설정하는 responsibillity을 가정합니다.

이러한 상황에서 내 솔루션은 속성을 읽기 전용으로 남겨두고 예외를 지원하기 위해 파생 클래스에 새로운 읽기 / 쓰기 속성을 추가하는 것입니다. 원래 속성을 재정의하면 새 속성의 값이 반환됩니다. 새 속성은이 예외의 컨텍스트를 올바르게 나타내는 적절한 이름을 가질 수 있습니다.

이것은 또한 기슈의 "오해가 자랄 수 있도록 힘들게한다"라는 유효한 언급을지지한다.


1
어떤 어색함는 암시되지 않을 것 ReadableMatrix아형을 가진 (두 인수 읽기 전용 인덱스 속성) 클래스 ImmutableMatrixMutableMatrix. 행렬을 읽기만하면되고 나중에 시간이 바뀌더라도 신경 쓰지 않는 코드는 유형의 매개 변수를 받아 들일 수 ReadableMatrix있으며 변경 가능하거나 변경 불가능한 하위 유형에 완벽하게 만족할 수 있습니다.
supercat

추가 세터는 클래스 내에서 개인 변수를 수정할 수 있습니다. 그런 다음 기본 클래스의 소위 "읽기 전용"속성은 여전히 ​​새 개인 변수를 읽음으로써 "클래스 내에서"값을 결정합니다. 거기에 문제가 보이지 않습니다. 예를 들어, 무엇을하는지 살펴보십시오 List<T>.Count. 할당 할 수 없지만 클래스의 사용자가 수정할 수 없다는 의미에서 "읽기 전용"을 의미하지는 않습니다. 목록 항목을 추가하고 제거하여 간접적이지만 아주 잘 수정할 수 있습니다.
또는 매퍼

-2

IL 레벨에서 읽기 / 쓰기 특성은 두 가지 (getter 및 setter) 메소드로 변환됩니다.

재정의하는 경우 기본 인터페이스를 계속 지원해야합니다. 세터를 추가 할 수 있다면 클래스의 인터페이스에 관한 한 외부 세계에서는 보이지 않는 새로운 메소드를 효과적으로 추가하게됩니다.

사실, 새로운 방법을 추가해도 호환성 자체가 깨지지는 않지만 숨겨져 있기 때문에이를 허용하지 않는 결정은 완벽합니다.


2
나는 여기서 "외부 세계"에 관심이 없다. 클래스에 기능을 추가하고 싶습니다.이 클래스는 기본이 아닌 특정 클래스를 알고있는 코드에만 항상 표시됩니다.
ripper234

@ ripper234 Matt bolt의 답변 참조 : 파생 클래스의 사용자 만 새 속성을 볼 수있게하려면 new대신 대신 사용해야 합니다 override.
Dan Berindei

1
숨겨져 있지 않습니다. 새 메서드는 "외부 세계에 숨겨져 있기 때문에"자식 클래스에 새 메서드를 추가 할 수 없다고 말하는 것과 같습니다. 물론 기본 클래스 관점에서 숨겨져 있지만 자식 클래스의 클라이언트에서 액세스 할 수 있습니다. (게터가 돌려 주어야 할 것에 영향을 미침)
Sheepy

Sheepy, 그건 내가 전혀 말하는 것이 아닙니다. 내 말은, 당신이 지적한대로 "기본 클래스 관점에서"숨겨져있을 것입니다. 그렇기 때문에 "클래스 인터페이스 에 관한 한"추가했습니다. 기본 클래스를 서브 클래 싱하는 경우 "외부 세계"가 기본 클래스 유형의 객체 인스턴스를 참조 할 것입니다. 그들이 당신의 구체적 클래스를 직접 사용한다면, 당신은 기본 클래스와의 호환성이 필요하지 않을 것이고, 당신은 반드시 new추가와 함께 사용할 수 있습니다.
Ishmaeel

-2

읽기 전용 속성 (세터 없음)이있는 클래스에는 그럴만한 이유가있을 수 있습니다. 예를 들어 기본 데이터 저장소가 없을 수 있습니다. 세터를 만들면 클래스가 지정한 계약이 중단됩니다. 그냥 나쁜 OOP입니다.


투표 이것은 매우 간결합니다. 기본 클래스의 소비자가 계약을 위반하도록 Microsoft가 왜 허용해야합니까? 나는 너무 길고 혼란스러운 대답의 매우 짧은 버전에 동의합니다. 롤 기본 클래스의 소비자라면 계약을 어 기고 싶지는 않지만 몇 가지 다른 방식으로 구현할 수 있습니다.
Suamere
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.