it-swarm-ko.tech

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

다음 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 접근자가 없기 때문에 재정의 할 수 없습니다.

138
ripper234

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

나는 이것에 대해 Microsoft와 함께 있습니다.
저는 Baseclass 파생물에 대해 코드를 작성하라는 새로운 프로그래머라고 가정하겠습니다. 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 속성을 외부에서 수정 한 경우이 클래스는 오래된 값을 제공 할 수 있습니다. 요점은 '열리고 비열한 일이나 사람들을 놀라게하지 말아라'

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

1
Gishu

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

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

getset이 모두 재정의된다는 것은 매우 명백합니다. 기본 클래스에는 set가 없으므로 컴파일러가 불평합니다. 기본 클래스에 정의되지 않은 메서드를 재정의 할 수없는 것처럼 setter도 재정의 할 수 없습니다.

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

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

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

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

public int MyProperty { override get; set; }
36
Roman Starkov

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

먼저 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를 사용하는 것에 대한 언급은 여기에 적용되지 않으며 단순히 작동하지 않으며 인터페이스를 통해 설명 된 가상 게터와 같은 원하는 결과를 얻지 못합니다.

19
JJJ

있을 수있다

tl; dr - 원하는 경우 setter로 get-only 메소드를 대체 할 수 있습니다. 기본적으로는 다음과 같습니다.

  1. 동일한 이름을 사용하여 newget을 모두 가진 set 특성을 작성하십시오.

  2. 다른 작업을 수행하지 않으면 파생 클래스가 기본 유형을 통해 호출 될 때 이전 get 메소드가 계속 호출됩니다. 이 문제를 해결하려면 이전 abstract 메서드에서 override를 사용하여 get 메서드의 결과를 반환하도록 get 중간 레이어를 추가하십시오.

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

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

  • 기본 정의가 get 전용 인 경우 더 파생 된 반환 유형을 사용할 수 있습니다.

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

  • 기본 정의가 이미 get/set 인 경우 :

    • 보다 파생 된 반환 유형을 사용할 수 있습니다. if set- only;

    • 덜 파생 된 반환 형식을 사용할 수 있습니다. if get- only로 지정하십시오.

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

상황 : 기존 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; } }
}

문제 : override/get에서만 getset 할 수 없습니다

override/get 속성을 사용하여 set을 (를) 원하지만 컴파일되지 않습니다.

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

솔루션 : abstract 중간 레이어를 사용하십시오.

override/get 속성을 사용하여 set을 직접 _ 수는 없지만 can

  1. 이름이 같은 newget/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; }
}

그런 다음 이전에 컴파일하지 않은 클래스를 작성하십시오. 실제로 override- only 속성을 get 'ing하지 않기 때문에 이번에는 컴파일됩니다; 대신 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.

토론

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

  1. 기본 클래스에 있던 속성에 관계없이 모든 속성을 get 전용, set 전용 또는 get- and -set 속성으로 변경하십시오. .

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

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

14
Nat

파생 형식에서 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;
   }
9
T.Tobler

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

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

1
supercat

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

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

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

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

1
Thomas Danecker

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

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

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

그럼에도 불구하고 사용 사례의 작은 하위 집합에 대한 솔루션이지만 C # 6.0에서는 "읽기 전용"설정자가 재정의 된 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;
    }
}
0
lxa

질문의 제목이 추상 속성을 재정의하는 것과 관련되거나 질문이 클래스의 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;}로 속성을 읽을 수있는 모든 클래스에는 해당 속성에 대해 SOME 일종의 노출 된 세터가 있습니다. 메타 데이터는 다음과 같습니다.

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
Suamere

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);
}

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

0
user2514880